From f78013671f78aa71d2749f2b83af78015ed9d1d0 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 9 May 2012 18:11:00 -0400 Subject: [PATCH] AMQP-201 Exchange-to-Exchange Binding The BindingFactoryBean already supported exchange to exchange binding. Add namespace support; remove 'required' from queue attribute in XSD; add 'exchange' (optional) and enforce XOR in parser. Builds on parser refactoring for Federated Exchange. Tested with a real broker with the amqp-sample. --- .../rabbit/config/AbstractExchangeParser.java | 19 +++++++++++++++++++ .../rabbit/config/DirectExchangeParser.java | 2 +- .../rabbit/config/FanoutExchangeParser.java | 2 +- .../rabbit/config/HeadersExchangeParser.java | 2 +- .../rabbit/config/TopicExchangeParser.java | 2 +- .../amqp/rabbit/config/spring-rabbit-1.0.xsd | 14 +++++++++++++- .../config/RabbitNamespaceHandlerTests.java | 2 +- .../RabbitNamespaceHandlerTests-context.xml | 1 + 8 files changed, 38 insertions(+), 6 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/AbstractExchangeParser.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/AbstractExchangeParser.java index 13b82e96..08f409eb 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/AbstractExchangeParser.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/AbstractExchangeParser.java @@ -21,6 +21,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; 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; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; @@ -42,6 +43,8 @@ public abstract class AbstractExchangeParser extends AbstractSingleBeanDefinitio protected static final String BINDING_QUEUE_ATTR = "queue"; + protected static final String BINDING_EXCHANGE_ATTR = "exchange"; + @Override protected boolean shouldGenerateIdAsFallback() { return true; @@ -87,4 +90,20 @@ public abstract class AbstractExchangeParser extends AbstractSingleBeanDefinitio protected abstract AbstractBeanDefinition parseBinding(String exchangeName, Element binding, ParserContext parserContext); + protected void parseDestination(Element binding, ParserContext parserContext, BeanDefinitionBuilder builder) { + String queueAttribute = binding.getAttribute(BINDING_QUEUE_ATTR); + String exchangeAttribute = binding.getAttribute(BINDING_EXCHANGE_ATTR); + boolean hasQueueAttribute = StringUtils.hasText(queueAttribute); + boolean hasExchangeAttribute = StringUtils.hasText(exchangeAttribute); + if (!(hasQueueAttribute ^ hasExchangeAttribute)) { + parserContext.getReaderContext().error("Binding must have exactly one of 'queue' or 'exchange'", binding); + } + if (hasQueueAttribute) { + builder.addPropertyReference("destinationQueue", queueAttribute); + } + if (hasExchangeAttribute) { + builder.addPropertyReference("destinationExchange", exchangeAttribute); + } + } + } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/DirectExchangeParser.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/DirectExchangeParser.java index 13743515..a57f9450 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/DirectExchangeParser.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/DirectExchangeParser.java @@ -40,7 +40,7 @@ public class DirectExchangeParser extends AbstractExchangeParser { protected AbstractBeanDefinition parseBinding(String exchangeName, Element binding, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(BindingFactoryBean.class); - builder.addPropertyReference("destinationQueue", binding.getAttribute(BINDING_QUEUE_ATTR)); + parseDestination(binding, parserContext, builder); builder.addPropertyValue("exchange", new TypedStringValue(exchangeName)); String bindingKey = binding.getAttribute(BINDING_KEY_ATTR); if (!StringUtils.hasText(bindingKey)) { diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/FanoutExchangeParser.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/FanoutExchangeParser.java index abbcaedb..99ce8561 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/FanoutExchangeParser.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/FanoutExchangeParser.java @@ -36,7 +36,7 @@ public class FanoutExchangeParser extends AbstractExchangeParser { @Override protected AbstractBeanDefinition parseBinding(String exchangeName, Element binding, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(BindingFactoryBean.class); - builder.addPropertyReference("destinationQueue", binding.getAttribute(BINDING_QUEUE_ATTR)); + parseDestination(binding, parserContext, builder); builder.addPropertyValue("exchange", new TypedStringValue(exchangeName)); builder.addPropertyValue("arguments", Collections.emptyMap()); return builder.getBeanDefinition(); diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/HeadersExchangeParser.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/HeadersExchangeParser.java index d59d3627..6c5f6005 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/HeadersExchangeParser.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/HeadersExchangeParser.java @@ -38,7 +38,7 @@ public class HeadersExchangeParser extends AbstractExchangeParser { @Override protected AbstractBeanDefinition parseBinding(String exchangeName, Element binding, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(BindingFactoryBean.class); - builder.addPropertyReference("destinationQueue", binding.getAttribute(BINDING_QUEUE_ATTR)); + parseDestination(binding, parserContext, builder); builder.addPropertyValue("exchange", new TypedStringValue(exchangeName)); ManagedMap map = new ManagedMap(); String key = binding.getAttribute("key"); diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/TopicExchangeParser.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/TopicExchangeParser.java index 3a5b9eff..6554185e 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/TopicExchangeParser.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/TopicExchangeParser.java @@ -41,7 +41,7 @@ public class TopicExchangeParser extends AbstractExchangeParser { @Override protected AbstractBeanDefinition parseBinding(String exchangeName, Element binding, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(BindingFactoryBean.class); - builder.addPropertyReference("destinationQueue", binding.getAttribute(BINDING_QUEUE_ATTR)); + parseDestination(binding, parserContext, builder); builder.addPropertyValue("exchange", new TypedStringValue(exchangeName)); builder.addPropertyValue("routingKey", new TypedStringValue(binding.getAttribute(BINDING_PATTERN_ATTR))); builder.addPropertyValue("arguments", Collections.emptyMap()); diff --git a/spring-rabbit/src/main/resources/org/springframework/amqp/rabbit/config/spring-rabbit-1.0.xsd b/spring-rabbit/src/main/resources/org/springframework/amqp/rabbit/config/spring-rabbit-1.0.xsd index 7451bc89..46aac742 100644 --- a/spring-rabbit/src/main/resources/org/springframework/amqp/rabbit/config/spring-rabbit-1.0.xsd +++ b/spring-rabbit/src/main/resources/org/springframework/amqp/rabbit/config/spring-rabbit-1.0.xsd @@ -441,7 +441,7 @@ - + + + + + + + + + + + diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/config/RabbitNamespaceHandlerTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/config/RabbitNamespaceHandlerTests.java index c9fb96fd..a5d2b0ab 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/config/RabbitNamespaceHandlerTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/config/RabbitNamespaceHandlerTests.java @@ -80,7 +80,7 @@ public final class RabbitNamespaceHandlerTests { public void testBindings() throws Exception { Map bindings = beanFactory.getBeansOfType(Binding.class); // 4 for each exchange type - assertEquals(16, bindings.size()); + assertEquals(17, bindings.size()); } @Test diff --git a/spring-rabbit/src/test/resources/org/springframework/amqp/rabbit/config/RabbitNamespaceHandlerTests-context.xml b/spring-rabbit/src/test/resources/org/springframework/amqp/rabbit/config/RabbitNamespaceHandlerTests-context.xml index a1ecb1b7..3c0bccf0 100644 --- a/spring-rabbit/src/test/resources/org/springframework/amqp/rabbit/config/RabbitNamespaceHandlerTests-context.xml +++ b/spring-rabbit/src/test/resources/org/springframework/amqp/rabbit/config/RabbitNamespaceHandlerTests-context.xml @@ -22,6 +22,7 @@ +