From 6afd9427e92834e75ff47fa101e81eff6223b1ba Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Thu, 1 Oct 2009 02:51:44 +0000 Subject: [PATCH] INT-762 Added "channel" and "publish-subscribe-channel" elements to the JMS namespace. --- .../jms/config/JmsChannelParser.java | 78 +++++++++ .../jms/config/JmsNamespaceHandler.java | 4 + .../jms/config/spring-integration-jms-2.0.xsd | 84 ++++++++++ .../config/JmsChannelParserTests-context.xml | 41 +++++ .../jms/config/JmsChannelParserTests.java | 156 ++++++++++++++++++ 5 files changed, 363 insertions(+) create mode 100644 org.springframework.integration.jms/src/main/java/org/springframework/integration/jms/config/JmsChannelParser.java create mode 100644 org.springframework.integration.jms/src/test/java/org/springframework/integration/jms/config/JmsChannelParserTests-context.xml create mode 100644 org.springframework.integration.jms/src/test/java/org/springframework/integration/jms/config/JmsChannelParserTests.java diff --git a/org.springframework.integration.jms/src/main/java/org/springframework/integration/jms/config/JmsChannelParser.java b/org.springframework.integration.jms/src/main/java/org/springframework/integration/jms/config/JmsChannelParser.java new file mode 100644 index 0000000000..387c9fe3b8 --- /dev/null +++ b/org.springframework.integration.jms/src/main/java/org/springframework/integration/jms/config/JmsChannelParser.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2009 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.jms.config; + +import org.w3c.dom.Element; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; + +/** + * Parser for the 'channel' and 'publish-subscribe-channel' elements of the + * Spring Integration JMS namespace. + * + * @author Mark Fisher + * @since 2.0 + */ +public class JmsChannelParser extends AbstractSingleBeanDefinitionParser { + + @Override + protected String getBeanClassName(Element element) { + return "org.springframework.integration.jms.JmsDestinationBackedMessageChannel"; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String connectionFactory = element.getAttribute("connection-factory"); + if (!StringUtils.hasText(connectionFactory)) { + connectionFactory = "connectionFactory"; + } + builder.addConstructorArgReference(connectionFactory); + if ("channel".equals(element.getLocalName())) { + this.parseDestination(element, parserContext, builder, "queue"); + } + else if ("publish-subscribe-channel".equals(element.getLocalName())) { + this.parseDestination(element, parserContext, builder, "topic"); + } + } + + private void parseDestination(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, String type) { + boolean isPubSub = "topic".equals(type); + String ref = element.getAttribute(type); + String name = element.getAttribute(type + "-name"); + boolean isReference = StringUtils.hasText(ref); + boolean isName = StringUtils.hasText(name); + if (!(isReference ^ isName)) { + parserContext.getReaderContext().error("Exactly one of the '" + type + + "' or '" + type + "-name' attributes is required.", element); + } + if (isReference) { + builder.addConstructorArgReference(ref); + } + else if (isName) { + builder.addConstructorArgValue(name); + builder.addConstructorArgValue(isPubSub); + String destinationResolver = element.getAttribute("destination-resolver"); + if (StringUtils.hasText(destinationResolver)) { + builder.addConstructorArgReference(destinationResolver); + } + } + } + +} diff --git a/org.springframework.integration.jms/src/main/java/org/springframework/integration/jms/config/JmsNamespaceHandler.java b/org.springframework.integration.jms/src/main/java/org/springframework/integration/jms/config/JmsNamespaceHandler.java index 25981bee2e..3f8c5b97ef 100644 --- a/org.springframework.integration.jms/src/main/java/org/springframework/integration/jms/config/JmsNamespaceHandler.java +++ b/org.springframework.integration.jms/src/main/java/org/springframework/integration/jms/config/JmsNamespaceHandler.java @@ -16,6 +16,7 @@ package org.springframework.integration.jms.config; +import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler; import org.springframework.integration.config.xml.SimpleHeaderEnricherParser; import org.springframework.integration.jms.JmsHeaders; @@ -33,6 +34,9 @@ public class JmsNamespaceHandler extends AbstractIntegrationNamespaceHandler { this.registerBeanDefinitionParser("inbound-channel-adapter", new JmsInboundChannelAdapterParser()); this.registerBeanDefinitionParser("outbound-gateway", new JmsOutboundGatewayParser()); this.registerBeanDefinitionParser("outbound-channel-adapter", new JmsOutboundChannelAdapterParser()); + BeanDefinitionParser channelParser = new JmsChannelParser(); + this.registerBeanDefinitionParser("channel", channelParser); + this.registerBeanDefinitionParser("publish-subscribe-channel", channelParser); this.registerBeanDefinitionParser("header-enricher", new SimpleHeaderEnricherParser(JmsHeaders.PREFIX, new String[] { "replyTo" })); } diff --git a/org.springframework.integration.jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.0.xsd b/org.springframework.integration.jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.0.xsd index 13da511377..02548e83e7 100644 --- a/org.springframework.integration.jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.0.xsd +++ b/org.springframework.integration.jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.0.xsd @@ -19,6 +19,90 @@ ]]> + + + + Defines a Message Channel that is backed by a JMS Queue. + + + + + + + + + Reference to a JMS Queue. Either this attribute or the 'queue-name' + must be provided, but only one. + + + + + + + Name of a JMS Queue to be resolved by this channel's DestinationResolver. + + + + + + + + + + + + Defines a Message Channel that is backed by a JMS Topic. + + + + + + + + + Reference to a JMS Topic. Either this attribute or the 'topic-name' + must be provided, but only one. + + + + + + + Name of a JMS Topic to be resolved by this channel's DestinationResolver. + + + + + + + + + + + + + ID for this channel. Required. + + + + + + + Reference to a JMS ConnectionFactory. If none is provided, the default + bean name for the reference will be "connectionFactory". + + + + + + + Reference to a DestinationResolver. If none is provided, the default will + be a DynamicDestinationResolver. + + + + + diff --git a/org.springframework.integration.jms/src/test/java/org/springframework/integration/jms/config/JmsChannelParserTests-context.xml b/org.springframework.integration.jms/src/test/java/org/springframework/integration/jms/config/JmsChannelParserTests-context.xml new file mode 100644 index 0000000000..0751b80ecb --- /dev/null +++ b/org.springframework.integration.jms/src/test/java/org/springframework/integration/jms/config/JmsChannelParserTests-context.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.springframework.integration.jms/src/test/java/org/springframework/integration/jms/config/JmsChannelParserTests.java b/org.springframework.integration.jms/src/test/java/org/springframework/integration/jms/config/JmsChannelParserTests.java new file mode 100644 index 0000000000..6e4384a688 --- /dev/null +++ b/org.springframework.integration.jms/src/test/java/org/springframework/integration/jms/config/JmsChannelParserTests.java @@ -0,0 +1,156 @@ +/* + * Copyright 2002-2009 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.jms.config; + +import static org.junit.Assert.assertEquals; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.Topic; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.core.MessageChannel; +import org.springframework.integration.jms.JmsDestinationBackedMessageChannel; +import org.springframework.jms.core.JmsTemplate; +import org.springframework.jms.listener.AbstractMessageListenerContainer; +import org.springframework.jms.support.destination.DestinationResolver; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Mark Fisher + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class JmsChannelParserTests { + + @Autowired + private MessageChannel queueReferenceChannel; + + @Autowired + private MessageChannel queueNameChannel; + + @Autowired + private MessageChannel queueNameWithResolverChannel; + + @Autowired + private Queue queue; + + @Autowired + private MessageChannel topicReferenceChannel; + + @Autowired + private MessageChannel topicNameChannel; + + @Autowired + private MessageChannel topicNameWithResolverChannel; + + @Autowired + private Topic topic; + + + @Test + public void queueReferenceChannel() { + assertEquals(JmsDestinationBackedMessageChannel.class, queueReferenceChannel.getClass()); + JmsDestinationBackedMessageChannel channel = (JmsDestinationBackedMessageChannel) queueReferenceChannel; + DirectFieldAccessor accessor = new DirectFieldAccessor(channel); + JmsTemplate jmsTemplate = (JmsTemplate) accessor.getPropertyValue("jmsTemplate"); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) accessor.getPropertyValue("container"); + assertEquals(queue, jmsTemplate.getDefaultDestination()); + assertEquals(queue, container.getDestination()); + } + + @Test + public void queueNameChannel() { + assertEquals(JmsDestinationBackedMessageChannel.class, queueNameChannel.getClass()); + JmsDestinationBackedMessageChannel channel = (JmsDestinationBackedMessageChannel) queueNameChannel; + DirectFieldAccessor accessor = new DirectFieldAccessor(channel); + JmsTemplate jmsTemplate = (JmsTemplate) accessor.getPropertyValue("jmsTemplate"); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) accessor.getPropertyValue("container"); + assertEquals("test.queue", jmsTemplate.getDefaultDestinationName()); + assertEquals("test.queue", container.getDestinationName()); + } + + @Test + public void queueNameWithResolverChannel() { + assertEquals(JmsDestinationBackedMessageChannel.class, queueNameWithResolverChannel.getClass()); + JmsDestinationBackedMessageChannel channel = (JmsDestinationBackedMessageChannel) queueNameWithResolverChannel; + DirectFieldAccessor accessor = new DirectFieldAccessor(channel); + JmsTemplate jmsTemplate = (JmsTemplate) accessor.getPropertyValue("jmsTemplate"); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) accessor.getPropertyValue("container"); + assertEquals("foo", jmsTemplate.getDefaultDestinationName()); + assertEquals("foo", container.getDestinationName()); + } + + @Test + public void topicReferenceChannel() { + assertEquals(JmsDestinationBackedMessageChannel.class, topicReferenceChannel.getClass()); + JmsDestinationBackedMessageChannel channel = (JmsDestinationBackedMessageChannel) topicReferenceChannel; + DirectFieldAccessor accessor = new DirectFieldAccessor(channel); + JmsTemplate jmsTemplate = (JmsTemplate) accessor.getPropertyValue("jmsTemplate"); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) accessor.getPropertyValue("container"); + assertEquals(topic, jmsTemplate.getDefaultDestination()); + assertEquals(topic, container.getDestination()); + } + + @Test + public void topicNameChannel() { + assertEquals(JmsDestinationBackedMessageChannel.class, topicNameChannel.getClass()); + JmsDestinationBackedMessageChannel channel = (JmsDestinationBackedMessageChannel) topicNameChannel; + DirectFieldAccessor accessor = new DirectFieldAccessor(channel); + JmsTemplate jmsTemplate = (JmsTemplate) accessor.getPropertyValue("jmsTemplate"); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) accessor.getPropertyValue("container"); + assertEquals("test.topic", jmsTemplate.getDefaultDestinationName()); + assertEquals("test.topic", container.getDestinationName()); + } + + @Test + public void topicNameWithResolverChannel() { + assertEquals(JmsDestinationBackedMessageChannel.class, topicNameWithResolverChannel.getClass()); + JmsDestinationBackedMessageChannel channel = (JmsDestinationBackedMessageChannel) topicNameWithResolverChannel; + DirectFieldAccessor accessor = new DirectFieldAccessor(channel); + JmsTemplate jmsTemplate = (JmsTemplate) accessor.getPropertyValue("jmsTemplate"); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) accessor.getPropertyValue("container"); + assertEquals("foo", jmsTemplate.getDefaultDestinationName()); + assertEquals("foo", container.getDestinationName()); + } + + + static class TestDestinationResolver implements DestinationResolver { + + @Autowired + private Queue queue; + + @Autowired + private Topic topic; + + public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain) + throws JMSException { + if (!"foo".equals(destinationName)) { + throw new IllegalStateException("only destination name of 'foo' is supported"); + } + return pubSubDomain ? topic : queue; + } + } + +}