diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/ConnectionFactoryParser.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/ConnectionFactoryParser.java new file mode 100644 index 00000000..a5a65cd9 --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/ConnectionFactoryParser.java @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2011 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.amqp.rabbit.config; + +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * @author Dave Syer + */ +class ConnectionFactoryParser extends AbstractSingleBeanDefinitionParser { + + private static final String CONNECTION_FACTORY_ATTRIBUTE = "connection-factory"; + + private static final String CHANNEL_CACHE_SIZE_ATTRIBUTE = "channel-cache-size"; + + private static final String HOST_ATTRIBUTE = "host"; + + private static final String PORT_ATTRIBUTE = "port"; + + private static final String VIRTUAL_HOST_ATTRIBUTE = "virtual-host"; + + private static final String USER_ATTRIBUTE = "username"; + + private static final String PASSWORD_ATTRIBUTE = "password"; + + @Override + protected Class getBeanClass(Element element) { + return CachingConnectionFactory.class; + } + + @Override + protected boolean shouldGenerateId() { + return false; + } + + @Override + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + NamespaceUtils.addConstructorArgParentRefIfAttributeDefined(builder, element, CONNECTION_FACTORY_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, CHANNEL_CACHE_SIZE_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, HOST_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, PORT_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, USER_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, PASSWORD_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, VIRTUAL_HOST_ATTRIBUTE); + + } + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/NamespaceUtils.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/NamespaceUtils.java index 0d479edc..906f05f5 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/NamespaceUtils.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/NamespaceUtils.java @@ -49,12 +49,14 @@ public abstract class NamespaceUtils { * @param attributeName the name of the attribute whose value will be used to populate the property * @param propertyName the name of the property to be populated */ - public static void setValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName, - String propertyName) { + public static boolean setValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, + String attributeName, String propertyName) { String attributeValue = element.getAttribute(attributeName); if (StringUtils.hasText(attributeValue)) { builder.addPropertyValue(propertyName, attributeValue); + return true; } + return false; } /** @@ -71,8 +73,9 @@ public abstract class NamespaceUtils { * @param element the XML element where the attribute should be defined * @param attributeName the name of the attribute whose value will be set on the property */ - public static void setValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName) { - setValueIfAttributeDefined(builder, element, attributeName, + public static boolean setValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, + String attributeName) { + return setValueIfAttributeDefined(builder, element, attributeName, Conventions.attributeNameToPropertyName(attributeName)); } @@ -95,17 +98,19 @@ public abstract class NamespaceUtils { * @param element the XML element where the attribute should be defined * @param attributeName the name of the attribute whose value will be used as a constructor argument */ - public static void addConstructorArgValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, + public static boolean addConstructorArgValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName) { String value = element.getAttribute(attributeName); if (StringUtils.hasText(value)) { builder.addConstructorArgValue(new TypedStringValue(value)); + return true; } + return false; } /** - * Populates the bean definition constructor argument with the boolean value of that attribute if it is defined in the given - * element or else uses the default provided. + * Populates the bean definition constructor argument with the boolean value of that attribute if it is defined in + * the given element or else uses the default provided. * * @param builder the bean definition builder to be configured * @param element the XML element where the attribute should be defined @@ -130,12 +135,34 @@ public abstract class NamespaceUtils { * @param element the XML element where the attribute should be defined * @param attributeName the name of the attribute whose value will be used to set the reference */ - public static void addConstructorArgRefIfAttributeDefined(BeanDefinitionBuilder builder, Element element, + public static boolean addConstructorArgRefIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName) { String value = element.getAttribute(attributeName); if (StringUtils.hasText(value)) { builder.addConstructorArgReference(value); + return true; } + return false; + } + + /** + * Populates the bean definition constructor argument with a reference to a bean with parent id equal to the + * attribute if it is defined in the given element. + * + * @param builder the bean definition builder to be configured + * @param element the XML element where the attribute should be defined + * @param attributeName the name of the attribute whose value will be used to set the reference + */ + public static boolean addConstructorArgParentRefIfAttributeDefined(BeanDefinitionBuilder builder, Element element, + String attributeName) { + String value = element.getAttribute(attributeName); + if (StringUtils.hasText(value)) { + BeanDefinitionBuilder child = BeanDefinitionBuilder.genericBeanDefinition(); + child.setParentName(value); + builder.addConstructorArgValue(child.getBeanDefinition()); + return true; + } + return false; } /** @@ -147,13 +174,16 @@ public abstract class NamespaceUtils { * @param attributeName the name of the attribute whose value will be used as a bean reference to populate the * property * @param propertyName the name of the property to be populated + * @return */ - public static void setReferenceIfAttributeDefined(BeanDefinitionBuilder builder, Element element, + public static boolean setReferenceIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName, String propertyName) { String attributeValue = element.getAttribute(attributeName); if (StringUtils.hasText(attributeValue)) { builder.addPropertyReference(propertyName, attributeValue); + return true; } + return false; } /** @@ -173,9 +203,9 @@ public abstract class NamespaceUtils { * * @see Conventions#attributeNameToPropertyName(String) */ - public static void setReferenceIfAttributeDefined(BeanDefinitionBuilder builder, Element element, + public static boolean setReferenceIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName) { - setReferenceIfAttributeDefined(builder, element, attributeName, + return setReferenceIfAttributeDefined(builder, element, attributeName, Conventions.attributeNameToPropertyName(attributeName)); } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/RabbitNamespaceHandler.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/RabbitNamespaceHandler.java index 1dada394..b1060aaf 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/RabbitNamespaceHandler.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/RabbitNamespaceHandler.java @@ -35,6 +35,8 @@ public class RabbitNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionParser("headers-exchange", new HeadersExchangeParser()); registerBeanDefinitionParser("listener-container", new ListenerContainerParser()); registerBeanDefinitionParser("admin", new AdminParser()); + registerBeanDefinitionParser("connection-factory", new ConnectionFactoryParser()); + registerBeanDefinitionParser("template", new TemplateParser()); } } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/TemplateParser.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/TemplateParser.java new file mode 100644 index 00000000..5baaee6e --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/config/TemplateParser.java @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2011 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.amqp.rabbit.config; + +import org.springframework.amqp.rabbit.core.RabbitTemplate; +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.w3c.dom.Element; + +/** + * @author Dave Syer + */ +class TemplateParser extends AbstractSingleBeanDefinitionParser { + + private static final String CONNECTION_FACTORY_ATTRIBUTE = "connection-factory"; + + private static final String EXCHANGE_ATTRIBUTE = "exchange"; + + private static final String QUEUE_ATTRIBUTE = "queue"; + + private static final String ROUTING_KEY_ATTRIBUTE = "routing-key"; + + private static final String REPLY_TIMEOUT_ATTRIBUTE = "reply-timeout"; + + private static final String MESSAGE_CONVERTER_ATTRIBUTE = "message-converter"; + + private static final String ENCODING_ATTRIBUTE = "encoding"; + + private static final String CHANNEL_TRANSACTED_ATTRIBUTE = "channel-transacted"; + + @Override + protected Class getBeanClass(Element element) { + return RabbitTemplate.class; + } + + @Override + protected boolean shouldGenerateId() { + return false; + } + + @Override + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String connectionFactoryRef = element.getAttribute(CONNECTION_FACTORY_ATTRIBUTE); + + if (!StringUtils.hasText(connectionFactoryRef)) { + parserContext.getReaderContext().error("A '" + CONNECTION_FACTORY_ATTRIBUTE + "' attribute must be set.", + element); + } + + if (StringUtils.hasText(connectionFactoryRef)) { + // Use constructor with connectionFactory parameter + builder.addConstructorArgReference(connectionFactoryRef); + } + + NamespaceUtils.setValueIfAttributeDefined(builder, element, CHANNEL_TRANSACTED_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, QUEUE_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, EXCHANGE_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, ROUTING_KEY_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, REPLY_TIMEOUT_ATTRIBUTE); + NamespaceUtils.setValueIfAttributeDefined(builder, element, ENCODING_ATTRIBUTE); + NamespaceUtils.setReferenceIfAttributeDefined(builder, element, MESSAGE_CONVERTER_ATTRIBUTE); + + } + +} 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 f3236f85..593f6642 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 @@ -1,8 +1,7 @@ - @@ -18,8 +17,7 @@ - + @@ -92,8 +90,7 @@ - + - + - + - + - + @@ -251,16 +244,14 @@ ]]> - + - + - + @@ -373,13 +363,13 @@ ]]> - + - + @@ -396,7 +386,7 @@ ]]> - + @@ -409,7 +399,7 @@ ]]> - + @@ -423,7 +413,7 @@ ]]> - + @@ -436,7 +426,7 @@ ]]> - + @@ -449,9 +439,9 @@ - - - + + + @@ -462,7 +452,7 @@ ]]> - + @@ -540,7 +530,7 @@ or defining the specified listener method. Required. ]]> - + @@ -600,8 +590,7 @@ ]]> - + @@ -615,4 +604,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/config/ConnectionFactoryParserTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/config/ConnectionFactoryParserTests.java new file mode 100644 index 00000000..d90711a6 --- /dev/null +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/config/ConnectionFactoryParserTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2011 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.amqp.rabbit.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.beans.factory.xml.XmlBeanFactory; +import org.springframework.core.io.ClassPathResource; + +/** + * + * @author Dave Syer + * + */ +public final class ConnectionFactoryParserTests { + + private XmlBeanFactory beanFactory; + + @Before + public void setUpDefaultBeanFactory() throws Exception { + beanFactory = new XmlBeanFactory(new ClassPathResource(getClass().getSimpleName() + "-context.xml", getClass())); + } + + @Test + public void testKitchenSink() throws Exception { + CachingConnectionFactory connectionFactory = beanFactory.getBean("kitchenSink", CachingConnectionFactory.class); + assertNotNull(connectionFactory); + assertEquals(10, connectionFactory.getChannelCacheSize()); + } + + @Test + public void testNative() throws Exception { + CachingConnectionFactory connectionFactory = beanFactory.getBean("native", CachingConnectionFactory.class); + assertNotNull(connectionFactory); + assertEquals(10, connectionFactory.getChannelCacheSize()); + } + +} diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/config/TemplateParserTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/config/TemplateParserTests.java new file mode 100644 index 00000000..d8de26a7 --- /dev/null +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/config/TemplateParserTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2011 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.amqp.rabbit.config; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.amqp.core.AmqpTemplate; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.SerializerMessageConverter; +import org.springframework.beans.factory.xml.XmlBeanFactory; +import org.springframework.core.io.ClassPathResource; + +/** + * + * @author Dave Syer + * + */ +public final class TemplateParserTests { + + private XmlBeanFactory beanFactory; + + @Before + public void setUpDefaultBeanFactory() throws Exception { + beanFactory = new XmlBeanFactory(new ClassPathResource(getClass().getSimpleName() + "-context.xml", getClass())); + } + + @Test + public void testTemplate() throws Exception { + AmqpTemplate template = beanFactory.getBean("template", AmqpTemplate.class); + assertNotNull(template); + } + + @Test + public void testKitchenSink() throws Exception { + RabbitTemplate template = beanFactory.getBean("kitchenSink", RabbitTemplate.class); + assertNotNull(template); + assertTrue(template.getMessageConverter() instanceof SerializerMessageConverter); + } + +} diff --git a/spring-rabbit/src/test/resources/org/springframework/amqp/rabbit/config/ConnectionFactoryParserTests-context.xml b/spring-rabbit/src/test/resources/org/springframework/amqp/rabbit/config/ConnectionFactoryParserTests-context.xml new file mode 100644 index 00000000..4ed92b34 --- /dev/null +++ b/spring-rabbit/src/test/resources/org/springframework/amqp/rabbit/config/ConnectionFactoryParserTests-context.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/spring-rabbit/src/test/resources/org/springframework/amqp/rabbit/config/TemplateParserTests-context.xml b/spring-rabbit/src/test/resources/org/springframework/amqp/rabbit/config/TemplateParserTests-context.xml new file mode 100644 index 00000000..275fa881 --- /dev/null +++ b/spring-rabbit/src/test/resources/org/springframework/amqp/rabbit/config/TemplateParserTests-context.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/docbkx/amqp.xml b/src/docbkx/amqp.xml index 6bef972a..123d4397 100644 --- a/src/docbkx/amqp.xml +++ b/src/docbkx/amqp.xml @@ -242,7 +242,8 @@ Connection connection = connectionFactory.createConnection();]]>When using XML, the configuration might look like this: - + @@ -266,7 +267,12 @@ Connection connection = connectionFactory.createConnection();]]>SingleConnectionFactory as useful for simple tests and maybe as a building block for extending the framework. - + A ConnectionFactory can be created + quickly and conveniently using the rabbit namespace: ]]> + In most cases this will be preferable since the framework can choose the + best defaults for you, and it will always choose a + CachingConnectionFactory.
@@ -692,13 +698,9 @@ Object receiveAndConvert(String queueName) throws AmqpException;]]>The RabbitMQ implementation of this interface is RabbitAdmin which when configured using Spring XML would look like this: - - - - + language="xml"> -]]> +]]> The RabbitAdmin implementation does automatic lazy declaration of Queues, @@ -733,10 +735,11 @@ Object receiveAndConvert(String queueName) throws AmqpException;]]>]]> To see how to use Java to configure the AMQP infrastructure, look at - the Stock sample application, there is the @Configuration class - AbstractStockRabbitConfiguration which in turn has - RabbitClientConfiguration and RabbitServerConfiguration subclasses. The - code for AbstractStockRabbitConfiguration is show below + the Stock sample application, there is the @Configuration + class AbstractStockRabbitConfiguration which in + turn has RabbitClientConfiguration and RabbitServerConfiguration + subclasses. The code for AbstractStockRabbitConfiguration is show + below
-