Search code examples
springshutdown

Why are SmartLifecycle components not shutting down?


In my webapp running in Tomcat, I have two Spring 5.0.2 classes that implement SmartLifecycle. They start as expected, but don't stop. I see the following in my log file:

INFO o.s.c.s.DefaultLifecycleProcessor$LifecycleGroup [localhost-startStop-2] Stopping beans in phase 2147483647
DEBUG o.s.c.s.DefaultLifecycleProcessor [localhost-startStop-2] Asking bean 'myQueue1' of type ... to stop
DEBUG o.s.c.s.DefaultLifecycleProcessor [localhost-startStop-2] Asking bean 'myQueue2' of type ... to stop
WARN o.s.c.s.DefaultLifecycleProcessor$LifecycleGroup [localhost-startStop-2] Failed to shut down 2 beans with phase value 2147483647 within timeout of 30000: [myQueue1, myQueue2]

I am running the Java process in the debugger, and I don't hit the breakpoint that is the first line in the stop() methods (another write to the log).

Here are the stop-related SmartLifeCycle methods I implemented (the same for both classes). Why isn't stop being executed? Any debugging tips are also welcome.

@Component
@Scope(value = "singleton")
public class MyQueue1 implements SmartLifecycle
{

@Override
public void stop(Runnable runnable) {
}

@Override
public void stop() {
    logger.info("Stop for " + queueName);
}

@Override
public boolean isRunning() {
    return queueThread != null;
}

@Override
public int getPhase() {
    return Integer.MAX_VALUE; // Suggest last to start; first to stop
}

}

Solution

  • Spring maintains a latch countdown to make sure that all "stop" methods have been completed. Here is the piece of code from DefaultLifeCycleProcessor.java

    ((SmartLifecycle)bean).stop(new Runnable() {
                                public void run() {
                                    latch.countDown();
                                    countDownBeanNames.remove(beanName);
                                    if(DefaultLifecycleProcessor.this.logger.isDebugEnabled()) {
                                        DefaultLifecycleProcessor.this.logger.debug("Bean '" + beanName + "' completed its stop procedure");
                                    }
                                }
                            });
    

    So in your code, because you are NOT calling this "run" method in passed runnable and because of that while executing below piece of code in DefaultLifecycleProcessor.java, latch countdown value is greater than 0. Hence you are getting the "failed.." warn log.

    try {
                        latch.await(this.timeout, TimeUnit.MILLISECONDS);
                        if(latch.getCount() > 0L && !countDownBeanNames.isEmpty() && DefaultLifecycleProcessor.this.logger.isWarnEnabled()) {
                            DefaultLifecycleProcessor.this.logger.warn("Failed to shut down " + countDownBeanNames.size() + " bean" + (countDownBeanNames.size() > 1?"s":"") + " with phase value " + this.phase + " within timeout of " + this.timeout + ": " + countDownBeanNames);
                        }
                    } catch (InterruptedException var5) {
                        Thread.currentThread().interrupt();
                    }
    

    Now, to solve the problem call below method in stop(Runnable runnable) method.

    runnable.run();