diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java index d60a447358..ef8685dd4d 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java @@ -17,6 +17,7 @@ package org.springframework.integration.http.config; import java.util.List; + import org.w3c.dom.Element; import org.springframework.beans.factory.BeanDefinitionStoreException; @@ -74,7 +75,7 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse throws BeanDefinitionStoreException { String id = super.resolveId(element, definition, parserContext); - if (!element.hasAttribute(getInputChannelAttributeName())) { + if (!this.expectReply && !element.hasAttribute("channel")) { // the created channel will get the 'id', so the adapter's bean name includes a suffix id = id + ".adapter"; } diff --git a/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsMessageDrivenEndpointParser.java b/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsMessageDrivenEndpointParser.java index 3276096978..54abca2327 100644 --- a/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsMessageDrivenEndpointParser.java +++ b/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsMessageDrivenEndpointParser.java @@ -18,14 +18,17 @@ package org.springframework.integration.jms.config; import org.w3c.dom.Element; +import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.springframework.integration.jms.JmsMessageDrivenEndpoint; +import org.springframework.jms.listener.DefaultMessageListenerContainer; import org.springframework.util.StringUtils; /** @@ -79,6 +82,22 @@ public class JmsMessageDrivenEndpointParser extends AbstractSingleBeanDefinition return JmsMessageDrivenEndpoint.class.getName(); } + @Override + protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) + throws BeanDefinitionStoreException { + String id = super.resolveId(element, definition, parserContext); + + if (!this.expectReply && !element.hasAttribute("channel")) { + // the created channel will get the 'id', so the adapter's bean name includes a suffix + id = id + ".adapter"; + } + if (!StringUtils.hasText(id)) { + id = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry()); + } + + return id; + } + @Override protected boolean shouldGenerateId() { return false; @@ -100,7 +119,11 @@ public class JmsMessageDrivenEndpointParser extends AbstractSingleBeanDefinition } private String parseMessageListenerContainer(Element element, ParserContext parserContext) { + String containerClass = element.getAttribute("container-class"); if (element.hasAttribute("container")) { + if (StringUtils.hasText(containerClass)) { + parserContext.getReaderContext().error("Cannot have both 'container' and 'container-class'", element); + } for (String containerAttribute : containerAttributes) { if (element.hasAttribute(containerAttribute)) { parserContext.getReaderContext().error("The '" + containerAttribute + @@ -110,8 +133,13 @@ public class JmsMessageDrivenEndpointParser extends AbstractSingleBeanDefinition return element.getAttribute("container"); } // otherwise, we build a DefaultMessageListenerContainer instance - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.jms.listener.DefaultMessageListenerContainer"); + BeanDefinitionBuilder builder; + if (StringUtils.hasText(containerClass)) { + builder = BeanDefinitionBuilder.genericBeanDefinition(containerClass); + } + else { + builder = BeanDefinitionBuilder.genericBeanDefinition(DefaultMessageListenerContainer.class); + } String destinationAttribute = this.expectReply ? "request-destination" : "destination"; String destinationNameAttribute = this.expectReply ? "request-destination-name" : "destination-name"; String pubSubDomainAttribute = this.expectReply ? "request-pub-sub-domain" : "pub-sub-domain"; @@ -198,7 +226,11 @@ public class JmsMessageDrivenEndpointParser extends AbstractSingleBeanDefinition IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel"); } else { - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "requestChannel"); + String channelName = element.getAttribute("channel"); + if (!StringUtils.hasText(channelName)) { + channelName = IntegrationNamespaceUtils.createDirectChannel(element, parserContext); + } + builder.addPropertyReference("requestChannel", channelName); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout", "requestTimeout"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-payload", "extractRequestPayload"); } diff --git a/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-3.0.xsd b/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-3.0.xsd index a3aedd4afa..6e967bed3a 100644 --- a/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-3.0.xsd +++ b/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-3.0.xsd @@ -1201,6 +1201,11 @@ + @@ -1208,6 +1213,22 @@ + + + + + + + + + + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java index 6c4d094a33..5f1475b3c6 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; import org.springframework.integration.core.PollableChannel; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.jms.JmsMessageDrivenEndpoint; @@ -38,6 +39,7 @@ import org.springframework.jms.support.destination.JmsDestinationAccessor; /** * @author Mark Fisher * @author Michael Bannister + * @author Gary Russell */ public class JmsMessageDrivenChannelAdapterParserTests { @@ -87,7 +89,7 @@ public class JmsMessageDrivenChannelAdapterParserTests { public void adapterWithTaskExecutor() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "jmsInboundWithTaskExecutor.xml", this.getClass()); - JmsMessageDrivenEndpoint endpoint = context.getBean("messageDrivenAdapter", JmsMessageDrivenEndpoint.class); + JmsMessageDrivenEndpoint endpoint = context.getBean("messageDrivenAdapter.adapter", JmsMessageDrivenEndpoint.class); DefaultMessageListenerContainer container = TestUtils.getPropertyValue(endpoint, "listenerContainer", DefaultMessageListenerContainer.class); assertSame(context.getBean("exec"), TestUtils.getPropertyValue(container, "taskExecutor")); @@ -95,52 +97,70 @@ public class JmsMessageDrivenChannelAdapterParserTests { } @Test - public void testGatewayWithReceiveTimeout() { + public void testAdapterWithReceiveTimeout() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "jmsInboundWithContainerSettings.xml", this.getClass()); - JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("adapterWithReceiveTimeout"); - gateway.start(); + JmsMessageDrivenEndpoint adapter = (JmsMessageDrivenEndpoint) context.getBean("adapterWithReceiveTimeout.adapter"); + adapter.start(); AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) - new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + new DirectFieldAccessor(adapter).getPropertyValue("listenerContainer"); assertEquals(1111L, new DirectFieldAccessor(container).getPropertyValue("receiveTimeout")); - gateway.stop(); + adapter.stop(); } @Test - public void testGatewayWithRecoveryInterval() { + public void testAdapterWithRecoveryInterval() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "jmsInboundWithContainerSettings.xml", this.getClass()); - JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("adapterWithRecoveryInterval"); - gateway.start(); + JmsMessageDrivenEndpoint adapter = (JmsMessageDrivenEndpoint) context.getBean("adapterWithRecoveryInterval.adapter"); + adapter.start(); AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) - new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + new DirectFieldAccessor(adapter).getPropertyValue("listenerContainer"); assertEquals(2222L, new DirectFieldAccessor(container).getPropertyValue("recoveryInterval")); - gateway.stop(); + adapter.stop(); } @Test - public void testGatewayWithIdleTaskExecutionLimit() { + public void testAdapterWithIdleTaskExecutionLimit() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "jmsInboundWithContainerSettings.xml", this.getClass()); - JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("adapterWithIdleTaskExecutionLimit"); - gateway.start(); + JmsMessageDrivenEndpoint adapter = (JmsMessageDrivenEndpoint) context.getBean("adapterWithIdleTaskExecutionLimit.adapter"); + adapter.start(); AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) - new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + new DirectFieldAccessor(adapter).getPropertyValue("listenerContainer"); assertEquals(7, new DirectFieldAccessor(container).getPropertyValue("idleTaskExecutionLimit")); - gateway.stop(); + adapter.stop(); } @Test - public void testGatewayWithIdleConsumerLimit() { + public void testAdapterWithIdleConsumerLimit() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "jmsInboundWithContainerSettings.xml", this.getClass()); - JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("adapterWithIdleConsumerLimit"); - gateway.start(); + JmsMessageDrivenEndpoint adapter = (JmsMessageDrivenEndpoint) context.getBean("adapterWithIdleConsumerLimit.adapter"); + adapter.start(); AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) - new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + new DirectFieldAccessor(adapter).getPropertyValue("listenerContainer"); assertEquals(33, new DirectFieldAccessor(container).getPropertyValue("idleConsumerLimit")); assertEquals(3, new DirectFieldAccessor(container).getPropertyValue("cacheLevel")); - gateway.stop(); + adapter.stop(); + } + + @Test + public void testAdapterWithContainerClass() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "jmsInboundWithContainerClass.xml", this.getClass()); + JmsMessageDrivenEndpoint adapter = context.getBean("adapterWithIdleConsumerLimit.adapter", JmsMessageDrivenEndpoint.class); + MessageChannel channel = context.getBean("adapterWithIdleConsumerLimit", MessageChannel.class); + assertSame(channel, TestUtils.getPropertyValue(adapter, "listener.gatewayDelegate.requestChannel")); + adapter.start(); + FooContainer container = TestUtils.getPropertyValue(adapter, "listenerContainer", FooContainer.class); + assertEquals(33, new DirectFieldAccessor(container).getPropertyValue("idleConsumerLimit")); + assertEquals(3, new DirectFieldAccessor(container).getPropertyValue("cacheLevel")); + adapter.stop(); + } + + public static final class FooContainer extends DefaultMessageListenerContainer { + } } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithContainerClass.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithContainerClass.xml new file mode 100644 index 0000000000..34bba45821 --- /dev/null +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithContainerClass.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/src/reference/docbook/jms.xml b/src/reference/docbook/jms.xml index 9e482ffe38..f8b8c5169d 100644 --- a/src/reference/docbook/jms.xml +++ b/src/reference/docbook/jms.xml @@ -87,12 +87,21 @@ message-driven Channel Adapter with a Destination reference. ]]> + The Message-Driven adapter also accepts several properties that pertain to the MessageListener container. - These values are only considered if you do not provide an actual 'container' reference. In that case, + These values are only considered if you do not provide a container reference. In that case, an instance of DefaultMessageListenerContainer will be created and configured based on these properties. For example, you can specify the "transaction-manager" reference, the "concurrent-consumers" value, and several other property references and values. Refer to the JavaDoc and Spring Integration's JMS Schema - (spring-integration-jms.xsd) for more detail. + (spring-integration-jms.xsd) for more details. + + + If you have a custom listener container implementation (usually a subclass of + DefaultMessageListenerContainer), you can either provide a reference to an instance + of it using the container attribute, or simply provide its fully qualified class name using + the container-class attribute. In that case, the attributes on the adapter + are transferred to an instance of your custom container. +