I'm configuring Spring Rabbit using an xml file for spring rabbit beans and configuration.
We use our own propertyGetter class to retrieve properties. In the spring rabbit xml below, it works everywhere except for one place. Why is this?
Below is the xml. The 'propertyGetter.get(...)' method being called is returning a String 'my.queue', but it seems that the RabbitMq xsd parser is trying to obtain a bean. (?).
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd">
<!-- This has the 'propertyGetter' bean -->
<import resource="**/applicationContext.xml"/>
<!-- RabbitMQ -->
<rabbit:connection-factory id="rabbitConnectionFactory" addresses="#{propertyGetter.get('rabbitMq.hostName')}" username="#{propertyGetter.get('rabbitMq.userName')}" password="#{propertyGetter.get('rabbitMq.password')}" virtual-host="#{propertyGetter.get('rabbitMq.vHost')}"/>
<rabbit:admin connection-factory="rabbitConnectionFactory"/>
<!-- RabbitMQ Template -->
<rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory" message-converter="messageConverter" exchange="#{propertyGetter.get('rabbitMq.exchange')}"/>
/*******************************************
*
* THIS IS THE ONLY PLACE IT DOESN'T WORK
* <rabbit:queue name=""
* It does seem to get value 'my.queue'
* but the exception that's posted
* below indicates it's trying to
* construct a bean! Why is this?
*******************************************/
<!-- RabbitMQ Queue Definitions -->
<!--<rabbit:queue name="my.queue" >--> <!--THIS WORKS-->
<rabbit:queue name="#{propertyGetter.get('rabbitMq.queue.name')}" > <!--THIS DOESN'T-->
<rabbit:queue-arguments>
<entry key="x-ha-policy" value="all"/>
</rabbit:queue-arguments>
</rabbit:queue>
<!-- RabbitMQ Exchange and Binding Definition -->
<rabbit:direct-exchange name="#{propertyGetter.get('rabbitMq.exchange')}">
<rabbit:bindings>
<rabbit:binding key="#{propertyGetter.get('rabbitMq.routing.key')}" queue="#{propertyGetter.get('rabbitMq.queue.name')}"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- Listeners -->
<rabbit:listener-container id="listener" connection-factory="rabbitConnectionFactory" concurrency="3" max-concurrency="5" requeue-rejected="false">
<rabbit:listener ref="eventNotificationAdapter" queues="#{propertyGetter.get('rabbitMq.queue.name')}" priority="#{propertyGetter.get('rabbitMq.queue.priority')}" />
</rabbit:listener-container>
<!-- Message Listener Adapters -->
<bean id="eventNotificationAdapter" class="org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter">
<constructor-arg name="delegate" ref="myQueueConsumer"/>
<constructor-arg name="messageConverter" ref="messageConverter"/>
<property name="defaultListenerMethod" value="process"/>
</bean>
<bean id="messageConverter" class="org.springframework.amqp.support.converter.JsonMessageConverter"/>
</beans>
The exception when the app is first initialized is'NoSuchBeanDefinitionException', but I'm not sure why whatever is parsing the xml is expecting a bean, when the method call is returning a String.
Here's the stack trace I get when trying to deploy the app:
SEVERE: Exception sending context initialized event to listener instance of class org.jboss.resteasy.plugins.spring.SpringContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.amqp.rabbit.config.BindingFactoryBean#0': Cannot resolve reference to bean '#{propertyGetter.get('rabbitMq.queue.name')}' while setting bean property 'destinationQueue'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'my.queue' is defined
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1222)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:736)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.jboss.resteasy.plugins.spring.SpringContextLoaderListener.contextInitialized(SpringContextLoaderListener.java:48)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5017)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5531)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1095)
at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1930)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'my.queue' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1168)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:281)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 29 more
Other notes: This is an xml file that uses the spring-rabbit-1.3.xsd
The solution does not use spring properties. This is for managing properties another way.
They are two different things.
For a <bean/>
, the name
attribute is an alias for the bean name. If no id
is provided, the first name
is used as the id.
For a <queue/>
the name
attribute is the name of the queue in the rabbitmq broker; the id
is always the bean name and, if not present, a unique bean name is generated.
There is some existing logic to register the name
as an alias, but that only works when the value is known at parse time (as you have found).
In fact, this use of aliasing is incorrect and causes other problems - for example you can't have an exchange and queue with the same name
- see this JIRA for a discussion about that.
To solve that issue, we are considering removing the aliasing altogether and requiring an id
attribute to give the bean a name.
I believe you can work-around your issue by giving the queue an id
and referencing that in your listener container queues
attribute, or change the container to use queue-names
instead.