Hello,

I'm facing an issue on Spring Boot 2.5.4 under the following conditions: * spring-boot-starter-parent:2.5.4 * spring-boot-starter-web:2.5.4 * spring-kafka:2.7.6 * embedded Tomcat won't start because Port 8080 is already in use

In this scenario an ApplicationFailedEvent is triggered and main thread is killed, but the application won't quit because kafka consumers threads are still alive. This results in a compromised application that appears like is up and running, but it is not. I have also made a test using legacy spring-boot-starter-parent:2.2.10.RELEASE and related dependencies. In this scenario the behaviour is different and the Spring boot application quits as expected.

I've published a simple project to reproduce the problem at this link https://github.com/tnormani/application-failed-event-testcase . Project requires port 8080 to be already bound by something else and a kafka broker for the consumer connection. In the project you will find also a ApplicationFailedListener I created during tests, but it's not referenced in the spring.factories file to make test case behave as expected.

I've also raised a question on StackOverflow (https://stackoverflow.com/questions/70378200/how-to-make-a-spring-boot-application-quit-on-tomcat-failure) days ago but after the comparison with spring-boot-starter-parent:2.2.10.RELEASE and some further testing, I thought it would be better if you could have a look at it.

I've already opened an issue to Spring-Kafka on this (https://github.com/spring-projects/spring-kafka/issues/2055) but they told me this a possible question for Spring Boot.

Thanks in advance.

Comment From: wilkinsona

Thanks for the sample, I've reproduced the problem.

The change in behaviour is due to a change in Spring boot 2.3 where the web server's lifecycle is now managed via a SmartLifecycle implementation. When a SmartLifecycle implementation fails to start and Framework is cleaning up the application context as a result, it would appear that other previously-started SmartLifecycle implementations are not called to stop(). I need to investigate further but my initial impression is that this may be a Spring Framework bug.

Comment From: wilkinsona

Looking at the javadoc of Lifecycle.stop(), I think this may, in fact, be a Spring Kafka bug:

on hot refresh during a context's lifetime or on aborted refresh attempts, a given bean's destroy method will be called without any consideration of stop signals upfront

What I have observed is that destroy() is called on KafkaListenerEndpointRegistry and its MLC isn't called as it doesn't implement DisposableBean. My reading of the javadoc for Lifecycle is that the registry needs to cope with a call to destroy() without stop() being called first and still clean itself up. I'll comment on spring-projects/spring-kafka#2055.