INT-3677: Support PPH and SpEL for Container Class

JIRA: https://jira.spring.io/browse/INT-3677

Previously, you could not use a property placeholder or SpEL when
defining the `container-class` attribute on message-driven endpoints.

Register the container with bean name `adapterId.container`.

Also add an alias for the container message listener `adapterId.listener`.

Add tests for JMS-backed channels - no code changes needed because a factory bean is used.
This commit is contained in:
Gary Russell
2015-05-11 11:03:17 +01:00
committed by Artem Bilan
parent 123b38a008
commit 093ab7924b
6 changed files with 98 additions and 19 deletions

View File

@@ -27,6 +27,7 @@ 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.ChannelPublishingJmsMessageListener;
import org.springframework.integration.jms.JmsMessageDrivenEndpoint;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.springframework.util.StringUtils;
@@ -111,8 +112,8 @@ public class JmsMessageDrivenEndpointParser extends AbstractSingleBeanDefinition
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String containerBeanName = this.parseMessageListenerContainer(element, parserContext);
String listenerBeanName = this.parseMessageListener(element, parserContext);
String containerBeanName = this.parseMessageListenerContainer(element, parserContext, builder.getRawBeanDefinition());
String listenerBeanName = this.parseMessageListener(element, parserContext, builder.getRawBeanDefinition());
builder.addConstructorArgReference(containerBeanName);
builder.addConstructorArgReference(listenerBeanName);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.AUTO_STARTUP);
@@ -121,7 +122,8 @@ public class JmsMessageDrivenEndpointParser extends AbstractSingleBeanDefinition
}
private String parseMessageListenerContainer(Element element, ParserContext parserContext) {
private String parseMessageListenerContainer(Element element, ParserContext parserContext,
BeanDefinition adapterBeanDefinition) {
String containerClass = element.getAttribute("container-class");
if (element.hasAttribute("container")) {
if (StringUtils.hasText(containerClass)) {
@@ -182,12 +184,16 @@ public class JmsMessageDrivenEndpointParser extends AbstractSingleBeanDefinition
"subscriptionName");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "client-id");
builder.addPropertyValue("autoStartup", false);
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
String beanName = adapterBeanNameRoot(element, parserContext, adapterBeanDefinition)
+ ".container";
parserContext.getRegistry().registerBeanDefinition(beanName, builder.getBeanDefinition());
return beanName;
}
private String parseMessageListener(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.jms.ChannelPublishingJmsMessageListener");
private String parseMessageListener(Element element, ParserContext parserContext,
BeanDefinition adapterBeanDefinition) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(ChannelPublishingJmsMessageListener.class);
builder.addPropertyValue("expectReply", this.expectReply);
if (this.expectReply) {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-channel");
@@ -233,11 +239,22 @@ public class JmsMessageDrivenEndpointParser extends AbstractSingleBeanDefinition
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "error-channel");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "message-converter");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "header-mapper");
String alias = adapterBeanNameRoot(element, parserContext, adapterBeanDefinition)
+ ".listener";
BeanDefinition beanDefinition = builder.getBeanDefinition();
String beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, parserContext.getRegistry());
BeanComponentDefinition component = new BeanComponentDefinition(beanDefinition, beanName);
BeanComponentDefinition component = new BeanComponentDefinition(beanDefinition, beanName, new String[] { alias });
parserContext.registerBeanComponent(component);
return beanName;
}
private String adapterBeanNameRoot(Element element, ParserContext parserContext,
BeanDefinition adapterBeanDefinition) {
String beanName = element.getAttribute(ID_ATTRIBUTE);
if (!StringUtils.hasText(beanName)) {
beanName = BeanDefinitionReaderUtils.generateBeanName(adapterBeanDefinition, parserContext.getRegistry());
}
return beanName;
}
}

View File

@@ -79,6 +79,9 @@
container-type="simple"/>
<jms:channel id="withContainerClass" queue-name="custom.container.queue"
container-class="org.springframework.integration.jms.config.JmsChannelParserTests$CustomTestMessageListenerContainer" />
container-class="${container.class}" />
<jms:channel id="withContainerClassSpEL" queue-name="custom.container.queue"
container-class="#{'org.springframework.integration.jms.config.JmsChannelParserTests$CustomTestMessageListenerContainer'}" />
</beans>

View File

@@ -17,8 +17,6 @@
package org.springframework.integration.jms.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import java.util.List;
@@ -117,6 +115,9 @@ public class JmsChannelParserTests {
@Autowired
private MessageChannel withContainerClass;
@Autowired
private MessageChannel withContainerClassSpEL;
@Autowired
private MessageBuilderFactory messageBuilderFactory;
@@ -300,6 +301,14 @@ public class JmsChannelParserTests {
assertEquals("custom.container.queue", container.getDestinationName());
}
@Test
public void withContainerClassSpEL() {
CustomTestMessageListenerContainer container = TestUtils.getPropertyValue(
withContainerClassSpEL, "container",
CustomTestMessageListenerContainer.class);
assertEquals("custom.container.queue", container.getDestinationName());
}
static class TestDestinationResolver implements DestinationResolver {

View File

@@ -173,6 +173,29 @@ public class JmsMessageDrivenChannelAdapterParserTests {
assertSame(channel, TestUtils.getPropertyValue(adapter, "listener.gatewayDelegate.requestChannel"));
adapter.start();
FooContainer container = TestUtils.getPropertyValue(adapter, "listenerContainer", FooContainer.class);
assertSame(container, context.getBean("adapterWithIdleConsumerLimit.container"));
assertEquals(33, new DirectFieldAccessor(container).getPropertyValue("idleConsumerLimit"));
assertEquals(3, new DirectFieldAccessor(container).getPropertyValue("cacheLevel"));
assertSame(context.getBean("adapterWithIdleConsumerLimit.listener"),
TestUtils.getPropertyValue(container, "messageListener"));
adapter.stop();
adapter = context.getBean("adapterWithIdleConsumerLimit2.adapter", JmsMessageDrivenEndpoint.class);
channel = context.getBean("adapterWithIdleConsumerLimit2", MessageChannel.class);
assertSame(channel, TestUtils.getPropertyValue(adapter, "listener.gatewayDelegate.requestChannel"));
adapter.start();
container = TestUtils.getPropertyValue(adapter, "listenerContainer", FooContainer.class);
assertSame(container, context.getBean("adapterWithIdleConsumerLimit2.container"));
assertEquals(33, new DirectFieldAccessor(container).getPropertyValue("idleConsumerLimit"));
assertEquals(3, new DirectFieldAccessor(container).getPropertyValue("cacheLevel"));
adapter.stop();
adapter = context.getBean("org.springframework.integration.jms.JmsMessageDrivenEndpoint#0", JmsMessageDrivenEndpoint.class);
channel = context.getBean("in", MessageChannel.class);
assertSame(channel, TestUtils.getPropertyValue(adapter, "listener.gatewayDelegate.requestChannel"));
adapter.start();
container = TestUtils.getPropertyValue(adapter, "listenerContainer", FooContainer.class);
assertSame(container, context.getBean("org.springframework.integration.jms.JmsMessageDrivenEndpoint#0.container"));
assertEquals(33, new DirectFieldAccessor(container).getPropertyValue("idleConsumerLimit"));
assertEquals(3, new DirectFieldAccessor(container).getPropertyValue("cacheLevel"));
adapter.stop();

View File

@@ -1,2 +1,3 @@
queue=testQueue
concurrency=5-25
concurrency=5-25
container.class=org.springframework.integration.jms.config.JmsChannelParserTests$CustomTestMessageListenerContainer

View File

@@ -3,21 +3,47 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/jms
http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<util:properties id="props">
<prop key="container.class">org.springframework.integration.jms.config.JmsMessageDrivenChannelAdapterParserTests$FooContainer</prop>
</util:properties>
<context:property-placeholder properties-ref="props"/>
<jms:message-driven-channel-adapter id="adapterWithIdleConsumerLimit"
connection-factory="testConnectionFactory"
destination-name="testQueue"
container-class="org.springframework.integration.jms.config.JmsMessageDrivenChannelAdapterParserTests$FooContainer"
container-class="${container.class}"
idle-consumer-limit="33"
cache-level="3"
auto-startup="false" />
<jms:message-driven-channel-adapter id="adapterWithIdleConsumerLimit2"
connection-factory="testConnectionFactory"
destination-name="testQueue"
container-class="#{props['container.class']}"
idle-consumer-limit="33"
cache-level="3"
auto-startup="false" />
<!-- no id -->
<jms:message-driven-channel-adapter channel="in"
connection-factory="testConnectionFactory"
destination-name="testQueue"
container-class="#{props['container.class']}"
idle-consumer-limit="33"
cache-level="3"
auto-startup="false" />
<si:channel id="in" />
<bean id="testConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<constructor-arg>
<bean class="org.springframework.integration.jms.StubConnection">