4

I'm working on a Spring boot JMS application that is setup strictly with bean annotations and is reading messages from WebshpereMQ. Everything works except I can't figure out how to safely shutdown this application. Once my JMSListener Method reads all the messages it just stays idle. I make an initial connection to the queue and access the queue depth, so ideally when queue depth is zero, it needs to wrap it up and shut down. My current workaround (and I do not like it at all) is this little method I call (from inside the listener, yikes) when depth is zero:

public void shutDownApplication() {
        logger.info("Initiating shutdown of application...");
        System.out.println("Terminating application...");
        Thread.currentThread().interrupt();
        System.exit(0);
    }

I do not like this solution. And neither does Spring, because this is obviously interrupted as an error mid-process and before the application dies my JMSListener initiates a rollback and puts the single last remaining message back on the queue.

I tried a few other solutions after looking at the following sources:

How can I Stop/start/Pause a @JmsListener (the clean way)

How to gracefully shut down a Spring JMS MessageListenerAdapter

http://forum.spring.io/forum/spring-projects/integration/jms/124980-graceful-shutdown-of-jms-message-listener

This was my most recent solution:

public class JMSShutdownService {

    public void initiateShutdown() {
        JmsListenerEndpointRegistry jmsListenerEndpointRegistry = new JmsListenerEndpointRegistry();
        Collection<MessageListenerContainer> col = jmsListenerEndpointRegistry
                .getListenerContainers();
        for (MessageListenerContainer cont : col) {
            cont.stop(Thread.currentThread());
        }
        System.exit(0);
    }

}

This kills the application but still puts the last message back on the queue. There are alot of intricacies of Spring I'm still trying to understand, so it all really comes down to that. I feel like the main issue is that it is inside the listener I signal for shutdown. From what I gather the listener shouldn't be responsible for that. But I'm not sure how to define a way to shutdown the application before the listener starts, or how to pop out of the listener when queue depth is zero.

Any ideas?

Community
  • 1
  • 1
LumberSzquatch
  • 934
  • 3
  • 19
  • 44

1 Answers1

2
JmsListenerEndpointRegistry jmsListenerEndpointRegistry = new JmsListenerEndpointRegistry();

That's no use; a new registry won't have any containers, if you are using @JmsListener you need to get the registry from the application context.

System.exit(0);

That just kills the JVM.

The bottom line is you should stop on the container on a different thread - use a task executor to start a new thread to stop the container; the container will wait for the thread to exit the listener before stopping.

After stopping the container, you will need to wait for a grace period before killing the JVM.

How do you really know you are done? Another message could show up after you stop the container, in which case you'll likely see some noise in the log about a message being rejected because the container is stopping.

EDIT

in my listener...

...
if (timeToShutDown()) {
    Executors.newSingleThreadExecutor.execute(new Runnable() {

        public void run() {
            stopTheContainer();
        }
    }
 }
 // exit the listener so the container can actually stop.
Gary Russell
  • 131,626
  • 14
  • 99
  • 125
  • ` use a task executor to start a new thread to stop the container`: When exactly does this new thread creation happen? Before the listener kicks off, or actually in the listener? Then this thread is the one that tells the container "hey I got the signal that we are done, shut it down"? – LumberSzquatch May 06 '16 at 18:08
  • Right - the listener can determine it's time to stop and invoke `Executors.newSingleThreadExecutor.execute(...)` then in the `Runnable` stop the container, that frees up the listener thread to exit and the container will wait for that before actually stopping. – Gary Russell May 06 '16 at 18:17
  • Last question, and I apologize for it as I think I know the answer but I want to confirm. When you say "and then in the Runnable", Runnable refers to the newly created thread, yeah? – LumberSzquatch May 06 '16 at 19:25
  • See my edit - the executor invokes a `Runnable` on the new thread. – Gary Russell May 06 '16 at 19:30
  • Just wanted to come back and say this worked like a dream. I also had to change a bean from `DefaultJmsListenerContainerFactory` to `DefaultMessageListenerContainer`. I don't know if that was completely necessary, but I couldn't seem to figure out how to get the container otherwise. I also had to set `autoStartup` to false to avoid errors in my logs. Everything works, but if you willing to provide input on these changes that would also be appreciated, but you have done a great deal for me already. – LumberSzquatch May 09 '16 at 13:51
  • When using the factory, the containers are not beans, but you can get a reference to them (by id or as a collection) from the registry bean - see [the documentation](http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/jms.html#jms-annotated) - see `JmsListenerEndpointRegistry.getListenerContainer*()` methods. I don't see how having a custom shutdown mechanism would cause problems with `autoStartup`. – Gary Russell May 09 '16 at 14:05
  • I see, thanks for the the source. With `autoStartup` at true my listener was processing messages, and it seemed to work fine, just in my logs after every message it processed I saw `IllegalStateException: No message listener specified`, as I understood something was being created before another. Setting `autoStartup` to false remedied the problem (on the container that is). – LumberSzquatch May 09 '16 at 14:17
  • Sounds like something is amiss with your configuration the containers won't start until all the injection is completed, so it "sounds" like you have some extra partially configured containers somehow. If you want me to help track that down, start a new question, showing all the configuration. – Gary Russell May 09 '16 at 14:21
  • Sure, that would be very helpful. – LumberSzquatch May 09 '16 at 14:24