5

I am new in Spring Framework and my questions are below:

I want to instantiate the DefaultMessageListenerContainer programmatically and the code that I use is:

DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(cf);
container.setDestination(Queue);
container.setMessageListener(Consumer);
container.setReceiveTimeout(-1);
container.setMaxConcurrentConsumers(15);
container.setConcurrentConsumers(10);
container.start();

Why do I have to shutdown manually the DefaultMessageListenerContainer when my project is undeployed? If i do not shutdown manually the container the consumers stay open on my queues.

When I try to shutdown manually the container (by calling container.shutdown()) the procedure stucks and the project does not continue. If i initialize the DefaultMessageListenerContainer without giving receiveTimeout the shutdown procedure is executed correctly. Is there any problem with setReceiveTimeout(-1)?

Lipis
  • 21,388
  • 20
  • 94
  • 121
pbal
  • 531
  • 1
  • 7
  • 19

4 Answers4

1

You only have to shutdown your listener manually because you've programmatically started it! If you use an ApplicationContext to load your Spring beans from xml, then shutting down the App Context will shutdown all the beans for you.

The simplest way I've found to control Spring loaded beans is to create a servlet that implements init() and destroy() methods from HttpServlet. Init() loads my Spring configuration from my xml files (i.e. master file called spring.xml), and caches the ApplicationContext object. Then destory() will call close() on the ApplicationContext. This will close/shutdown all the Spring beans (i.e. your JMS listeners will get stopped).

Any particular reason you're programmatically creating your listeners?

shuttsy
  • 1,585
  • 2
  • 19
  • 34
  • 1
    Thank you for your answer!I want to create my listeners programmatically because i want to change some attributes on run-time. These attributes are: the concurrent consumer,the max concurrent consumers per queue, the receive timeout. In this way i can give my attributes via JMX and restart the listeners with new values. The class that instantiate my listeners implements SmartLifecycle. So the listeners are initialized on the startup and destroyed when the project undeployed. The problem is that when i set -1 in setReceiveTimeout method then the project stuck when the listeners try to shutdown – pbal Jan 29 '13 at 08:01
  • Looking at the Spring docs for this class, I think you need to call stop() to stop the listeners, as doShutdown() just seems to unregisters the JMS consumers, which might hang as your consumers is constantly receiving (due to timeout = -1). – shuttsy Jan 29 '13 at 09:41
  • I tried to stop the listeners before doShutdown() but the problem is still here. The project stucks when the doShutDown() method is executed. – pbal Feb 19 '13 at 09:01
1

receiveTimeout is the problem. To shutdown, the container must have a chance to stop listening to the queue. If your consumer thread has an infinite timeout, it will continue listening and never check to see if the container needs to shutdown. Your container will take up to the receiveTimeout to shutdown. If it is -1, it will never shutdown.

Daniel Blezek
  • 4,539
  • 1
  • 19
  • 20
0

What you need here is to be able to just stop the container (not shut it down or unregister it) and be able to start it back up when you want to, all at runtime. Just use the .start() and .stop(), which are methods inherited from AbstractJmsListeningContainer I think. And do not mix these with .doStart(), .shutDown()... See the spring documentation.

With your listener wired via Spring, you can grab it anytime from the context and run a .stop or .start on it. During the Spring autowiring, you may set the property autoStartup to false, and the listenerContainer will be initialized but will not do any listening at startup.

eadjei
  • 2,066
  • 2
  • 14
  • 7
0

This is what I did to dynamically create new Listener and let Spring handle shutdown procedure

<beans>
//other beans
<bean id="importReadQueueDestination" class="org.springframework.jndi.JndiObjectFactoryBean"> 
        <property name="jndiName"><value>queue/dummyQueue</value></property> 
        <property name="resourceRef"><value>true</value></property>
    </bean>

    <bean id="importQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> 
        <property name="jndiName"><value>ConnectionFactory</value></property> 
        <property name="resourceRef"><value>true</value></property> 
    </bean>

     <bean id="importReadQueueSenderService" class="com.localhost.ImportReadQueueSenderService" scope="prototype"/>

    <!-- this is the Message Driven POJO (MDP) -->
    <bean id="importReadMessageListener" class="com.localhost.listener.ImportReadMessageListener" scope="prototype"/>

    <!-- and this is the message listener container -->
    <bean id="importReadJmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer" scope="prototype">
        <property name="connectionFactory" ref="importQueueConnectionFactory" />
        <property name="destination" ref="importReadQueueDestination" />
        <property name="messageListener" ref="importReadMessageListener" />
        <property name="concurrentConsumers" value="1"/>
</bean>

</beans>

Here, I've created a dummy queue queue/dummyQueue since DMLC requires to set either destination or destinationName property.
ImportReadMessageListener extends MessageListener. Java code to create and cache dynamic listeners

//Actual queue name where I need to send message. `tenantStore` is obtained from ThreadLocalObject
String queue = tenantStore.getProperty("importReadQueue");
//Obtaining existing senderService for that queue
Object queueSenderService = AppConfigurationManager.getQueueSenderService(queue);
if (queueSenderService != null) {
    ((IImportReadQueueSenderService) queueSenderService).sendObjectMessage(importReadQueueDO);
} else {
    // In-case of call received from new tenant, then dynamically create and cache a separate listener and DMLC for it
        InitialContext ic = new InitialContext();
        Queue destination = (Queue) ic.lookup(queue);
        ConnectionFactory importQueueConnectionFactory =(ConnectionFactory) ServiceContext.getBean("importQueueConnectionFactory");

        JmsTemplate importJmsTemplate=new JmsTemplate(importQueueConnectionFactory);
        importJmsTemplate.setDefaultDestination(destination);

        Object importReadMessageListener = ServiceContext.getBean("importReadMessageListener");

        DefaultMessageListenerContainer dmlc = (DefaultMessageListenerContainer)ServiceContext.getBean("importReadJmsContainer");
        dmlc.setDestination(destination);
    /*  below two steps are extremely important else you won't receive any message.  
        I already wasted a day behind this.*/
    //  https://stackoverflow.com/a/21364885/4800126
        dmlc.afterPropertiesSet();
        dmlc.start();

        IImportReadQueueSenderService importQueueSenderService = (IImportReadQueueSenderService) ServiceContext
                .getBean("importReadQueueSenderService");

        AppConfigurationManager.cacheQueueDetails(queue, dmlc, importQueueSenderService,
                importReadMessageListener);
        importQueueSenderService.setJmsTemplate(importJmsTemplate);
        importQueueSenderService.sendObjectMessage(importReadQueueDO);
    }  

So now when you close the application Spring will automatically shutdown all the listeners.

Kainix
  • 1,186
  • 3
  • 21
  • 33