Rajkishan Swami
Rajkishan Swami

Reputation: 3759

Adding Dynamic Number of Listeners(Spring JMS)

I have a requirement to add multiple listeners as mentioned in the application.properties file. Like Below,

InTopics=Sample.QUT4,Sample.T05,Sample.T01,Sample.JT7

NOTE: This number can be lot more or less.

I am thinking of getting them in an array,

@Value("${InTopics}")
private String[] inTopics;

But i don't know how to create multiple listeners from the array.

Currently, for one Topic i am doing as below,

@Configuration
@EnableJms
public class JmsConfiguration {

@Value("${BrokerURL}")
private String brokerURL;

@Value("${BrokerUserName}")
private String brokerUserName;

@Value("${BrokerPassword}")
private String brokerPassword;

@Bean
TopicConnectionFactory connectionFactory() throws JMSException {
    TopicConnectionFactory connectionFactory = new TopicConnectionFactory(brokerURL, brokerUserName, brokerPassword);
    return connectionFactory;
}

@Bean
JmsListenerContainerFactory<?> jmsContainerFactory(TopicConnectionFactory connectionFactory) throws JMSException {
    SimpleJmsListenerContainerFactory factory = new SimpleJmsListenerContainerFactory();
    factory.setConnectionFactory(connectionFactory);
    factory.setPubSubDomain(Boolean.TRUE);
    return factory;
 }

}

And My Listener,

@JmsListener(destination = "${SingleTopicName}", containerFactory = "jmsContainerFactory")
public void receiveMessage(Message msg) {
   //Do Some Stuff
}

Is there any way i can achieve this?

Upvotes: 13

Views: 20656

Answers (3)

Olgun Kaya
Olgun Kaya

Reputation: 2579

I did not know this was exist and I had to manually write all these code down. So, another way to do this is to implement BeanFactoryPostProcessor in your bean and manually add all the required components of a jms listener.

  1. jndiTemplate
  2. jndiQueueConnectionFactory (depends on jndiTemplate from step 1)
  3. queueConnectionFactory (depends on jndiQueueConnectionFactory from step 2)
  4. jndiDestinationResolver (uses jndiTemplate from stem 1)
  5. messageListenerContiner (uses all above created items)

so, as you can see I do not only multiply the jms listener, I als dynamically generate the listener container multiple too. Ofc, this was my requirement. And may vary based on requirements.

One thing to keep in mind is that, there is no resource (like properties etc.. loaded) while you manipulate the BeanFactoryPostProcessor. You have to manually load the properties. I did via, afterPropertiesSet method coming from InitializingBean

Upvotes: 0

manojdevendran
manojdevendran

Reputation: 21

Here is the customized code to define number of listeners dynamically.

JmsConfiguration jmsConfiguration;

private List<String> queueList;

@Bean
public DefaultJmsListenerContainerFactory mqJmsListenerContainerFactory() throws JMSException {
    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
    factory.setConnectionFactory(jmsConfiguration.jmsConnectionFactory());
    factory.setDestinationResolver(new DynamicDestinationResolver());
    factory.setSessionTransacted(true);
    factory.setConcurrency("5");
    return factory;
}

@Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {

    queueList.forEach(queue -> {
        SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
        endpoint.setId(queue);
        endpoint.setDestination(queue);
        try {
            endpoint.setMessageListener(message -> {
                try {
                    logger.info("Receieved ID: {} Destination {}", message.getJMSMessageID(), message.getJMSDestination());
                }
                catch (JMSException e) {
                    logger.info("Exception while reading message - " + e);
                }
            });
            registrar.setContainerFactory(mqJmsListenerContainerFactory());
        }
        catch (JMSException e) {
            logger.info("Exception - " + e);
        }
        registrar.registerEndpoint(endpoint);
    });

}

Upvotes: 2

Gary Russell
Gary Russell

Reputation: 174484

You can't do it with annotated @JmsListeners but you can register each listener programmatically by extending JmsListenerConfigurer as described in the reference documentation.

EDIT

Since you are injecting the property as an array...

@Value("${InTopics}")
private String[] inTopics;

Spring will split up the list an create an array based on the number of queues in the list.

You can then iterate over the array in JmsListenerConfigurer.configureJmsListeners() and create an endpoint for each element in the array - you don't need to know ahead of time how big the array is.

for (String inTopic : inTopics) {
    ...
}

Upvotes: 18

Related Questions