I have not been able to find any guidance online or the forum on the following problem.
I have some common code in a Jar file where I declare the following:
I then have a message producer in WebappA that declares the following:Code:@Configuration public class CommonConfig { @Bean public ConnectionFactory connectionFactory() { final CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost"); return connectionFactory; } @Bean public AmqpAdmin amqpAdmin() { final RabbitAdmin admin = new RabbitAdmin(rabbitConnectionFactory()); return admin; } public SimpleMessageListenerContainer configureContainer(Object messageHandler, String... queuesNames){ SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); container.setConnectionFactory(connectionFactory()); container.setQueueNames(queueNames) container.setMessageListener(new MessageListenerAdapter(messageHandler)); return container; } }
Then there is a message consumer in WebappB with the following:Code:@Configuration public class ServerConfig extends CommonConfig { @Bean public Exchange myExchange(){ return new TopicExchange("foobarExchange"); } }
My problem here is that both the server and client webapps are started at the same time. Since the exchange is declared in the ServerConfig file, the exchange won't exist until the message producer produces its first message since the AmqpAdmin bean creates the exchange lazily.Code:@Configuration public class ClientConfig extends CommonConfig { @Bean public MyMessageHandler myMessageHandler(){ return new MyMessageHandler(); } @Bean public SimpleMessageListenerContainer listenerContainer() { return configureContainer(myMessageHandler(), myQueue().getName()) } @Bean public Queue myQueue(){ return new Queue("myQueue"); } @Bean public Binding myBinding(){ return new Binding(myQueue().getName(), DestinationType.QUEUE, "foobarExchange", "abc.def", Collections.<String, Object> emptyMap()); } }
So then the binding of the Queue to the exchange in the client will fail.
This issue will probably repeat itself in other places as I have many webapps that declare Exchanges and other webapps that consume from them. I rather have the owning webapps of an Exchange declare that exchange instead of the consumer.
My reaction was to change the AmqpAdmin Bean in the CommonConfig to the following:
I am wondering if I will encounter any issues with this approach. So far my preliminary tests tell me no.Code:@Bean(initMethod = "initialize") public AmqpAdmin amqpAdmin() { final RabbitAdmin admin = new RabbitAdmin(rabbitConnectionFactory()); admin.setAutoStartup(false); return admin; }
The other thing I thought about doing was declaring the exchange as follows in the ServerConfig:
What about this approach? Which would be best? My doubt in all this is in calling the broker during the initialization of the application context. I don't want to run into any issues.Code:@Configuration public class ServerConfig extends CommonConfig { @Bean public Exchange myExchange(){ Exchange ex = new TopicExchange("foobarExchange"); amqpAdmin().declareExchange(ex); return ex; } }
My last resort option is to declare the exchange in the constructor or init method of the message producer:
The reason I rather not do this is because in the common config (which is part of even more common code), I rather not expose the internal of communicating with the broker. WebappA and WebappB and WebappX should just need to deal with creating exchanges, queues, and bindings. All webapps are following a similar approach to message consumption so even the configuration of the SimpleMessageListenerContainer is standardized so I am providing a convenience function to create one in the common config.Code:@Component public class MyMessageProducerImpl implements MyMessageProducer, InitializingBean { @Resource(name="amqpAdmin") private AmqpAdmin admin; @Resource(name="myExchange") private Exchange myExchange; @Override public void afterPropertiesSet() { admin.declareExchange(myExchange); } }
Any thoughts on all of this? It is possible that I will distribute the common code to other apps where another instance of the RabbitMQ broker exists and so I don't want to include the bean definitions of every Exchange in the common code as other RabbitMQ instances don't need to declare those exchanges.


Reply With Quote
