diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/ServiceActivatorFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/ServiceActivatorFactoryBean.java index 77932a7973..5401c17422 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/ServiceActivatorFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/ServiceActivatorFactoryBean.java @@ -73,7 +73,7 @@ public class ServiceActivatorFactoryBean extends AbstractStandardMessageHandlerF } /* * Return a reply-producing message handler so that we still get 'produced no reply' messages - * and the super class will inject the advice chain to advise the handler if needed. + * and the super class will inject the advice chain to advise the handler method if needed. */ handler = new AbstractReplyProducingMessageHandler() { 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 index 5b2da86780..508e3cc386 100644 --- 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 @@ -22,19 +22,19 @@ - - - diff --git a/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests-fail-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests-fail-context.xml new file mode 100644 index 0000000000..abbc47f5fb --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests-fail-context.xml @@ -0,0 +1,19 @@ + + + + + + + + + + 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 4756857945..f788c76c3c 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 @@ -18,11 +18,16 @@ package org.springframework.integration.handler; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.channel.QueueChannel; @@ -149,6 +154,22 @@ public class ServiceActivatorDefaultFrameworkMethodTests { assertEquals("processorTestInputChannel,processorTestService", reply.getHeaders().get("history").toString()); } + @Test + public void testFailOnDoubleReference() { + try { + new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-fail-context.xml", + this.getClass()); + fail("Expected exception due to 2 endpoints referencing the same bean"); + } + catch (Exception e) { + assertThat(e, Matchers.instanceOf(BeanCreationException.class)); + assertThat(e.getCause(), Matchers.instanceOf(BeanCreationException.class)); + assertThat(e.getCause().getCause(), Matchers.instanceOf(IllegalArgumentException.class)); + assertThat(e.getCause().getCause().getMessage(), + Matchers.containsString("An AbstractReplyProducingMessageHandler may only be referenced once")); + } + + } @SuppressWarnings("unused") private static class TestReplyingMessageHandler extends AbstractReplyProducingMessageHandler { diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java index dcf5433cab..634aeede57 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java @@ -27,6 +27,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; import javax.management.DynamicMBean; @@ -80,6 +81,8 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.util.Assert; import org.springframework.util.PatternMatchUtils; import org.springframework.util.ReflectionUtils; +import org.springframework.util.ReflectionUtils.FieldCallback; +import org.springframework.util.ReflectionUtils.FieldFilter; /** *

@@ -106,6 +109,7 @@ import org.springframework.util.ReflectionUtils; * @author Helena Edelson * @author Oleg Zhurakousky * @author Gary Russell + * @author Artem Bilan */ @ManagedResource public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostProcessor, BeanFactoryAware, @@ -217,7 +221,7 @@ public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostP @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { super.setBeanFactory(beanFactory); - Assert.isTrue(beanFactory instanceof ListableBeanFactory, "A ListableBeanFactory is required."); + Assert.isInstanceOf(ListableBeanFactory.class, beanFactory, "A ListableBeanFactory is required."); this.beanFactory = (ListableBeanFactory) beanFactory; } @@ -246,6 +250,12 @@ public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostP } if (bean instanceof MessageHandler) { + if (this.handlerInAnonymousWrapper(bean) != null) { + if (logger.isDebugEnabled()) { + logger.debug("Skipping " + beanName + " because it wraps another handler"); + } + return bean; + } SimpleMessageHandlerMetrics monitor = new SimpleMessageHandlerMetrics((MessageHandler) bean); Object advised = applyHandlerInterceptor(bean, monitor, beanClassLoader); handlers.add(monitor); @@ -282,6 +292,33 @@ public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostP } + private MessageHandler handlerInAnonymousWrapper(final Object bean) { + if (bean != null && bean.getClass().isAnonymousClass()) { + final AtomicReference wrapped = new AtomicReference(); + ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() { + + @Override + public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { + field.setAccessible(true); + Object handler = field.get(bean); + if (handler instanceof MessageHandler) { + wrapped.set((MessageHandler) handler); + } + } + }, new FieldFilter() { + + @Override + public boolean matches(Field field) { + return wrapped.get() == null && field.getName().startsWith("val$"); + } + }); + return wrapped.get(); + } + else { + return null; + } + } + /** * Copy of private method in super class. Needed so we can avoid using the bean factory to extract the bean again, * and risk it being a proxy (which it almost certainly is by now). @@ -987,20 +1024,22 @@ public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostP String source = "endpoint"; Object endpoint = null; + MessageHandler messageHandler = monitor.getMessageHandler(); + for (String beanName : names) { endpoint = beanFactory.getBean(beanName); - Object field = null; try { - field = extractTarget(getField(endpoint, "handler")); + Object field = extractTarget(getField(endpoint, "handler")); + if (field == messageHandler || + this.extractTarget(this.handlerInAnonymousWrapper(field)) == messageHandler) { + name = beanName; + endpointName = beanName; + break; + } } catch (Exception e) { logger.trace("Could not get handler from bean = " + beanName); } - if (field == monitor.getMessageHandler()) { - name = beanName; - endpointName = beanName; - break; - } } if (name != null && endpoint != null && name.startsWith("_org.springframework.integration")) { name = getInternalComponentName(name); @@ -1044,11 +1083,11 @@ public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostP } if (name == null) { - if (monitor.getMessageHandler() instanceof NamedComponent) { - name = ((NamedComponent) monitor.getMessageHandler()).getComponentName(); + if (messageHandler instanceof NamedComponent) { + name = ((NamedComponent) messageHandler).getComponentName(); } if (name == null) { - name = monitor.getMessageHandler().toString(); + name = messageHandler.toString(); } source = "handler"; } 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 index a222fe42dc..0e5a5f7c13 100644 --- 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 @@ -27,19 +27,19 @@ - - - diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests-fail-context.xml b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests-fail-context.xml new file mode 100644 index 0000000000..942235110a --- /dev/null +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests-fail-context.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + 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 index 90ad33084d..0f8db9084a 100644 --- 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 @@ -18,11 +18,16 @@ package org.springframework.integration.jmx; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.channel.QueueChannel; @@ -151,6 +156,23 @@ public class ServiceActivatorDefaultFrameworkMethodTests { assertEquals("processorTestInputChannel,processorTestService", reply.getHeaders().get("history").toString()); } + @Test + public void testFailOnDoubleReference() { + try { + new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-fail-context.xml", + this.getClass()); + fail("Expected exception due to 2 endpoints referencing the same bean"); + } + catch (Exception e) { + assertThat(e, Matchers.instanceOf(BeanCreationException.class)); + assertThat(e.getCause(), Matchers.instanceOf(BeanCreationException.class)); + assertThat(e.getCause().getCause(), Matchers.instanceOf(IllegalArgumentException.class)); + assertThat(e.getCause().getCause().getMessage(), + Matchers.containsString("An AbstractReplyProducingMessageHandler may only be referenced once")); + } + + } + private interface Foo { public String foo(String in); @@ -181,7 +203,7 @@ public class ServiceActivatorDefaultFrameworkMethodTests { public void handleMessage(Message requestMessage) { Exception e = new RuntimeException(); StackTraceElement[] st = e.getStackTrace(); - assertEquals("doDispatch", st[28].getMethodName()); + assertEquals("doDispatch", st[16].getMethodName()); } }