Terminated Spring Boot App in Eclipse - Shutdown hook not called
ExitCodeGenerator
requires the application to call the exit
method
System.exit(SpringApplication
.exit(SpringApplication.run(SampleBatchApplication.class, args)));
You could register additionally a shutdown hook. Hooks will be called
- when the VM is terminated normally (
System.exit
). - in response to an interrupt (
Ctrl+C
,SIGINT
) or signal (SIGHUP
,SIGTERM
).
In some circumstances, if the VM does not shut down cleanly (SIGKILL
, internal VM errors, errors in native methods), there's no guarantee whether shutdown hooks are called or not.
The code for your EmbeddedRedis
component could look like:
@PostConstruct
public void startRedis() throws IOException {
redisServer = new RedisServer(redisPort);
redisServer.start();
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
redisServer.stop();
}
});
}
I had the same problem, though not with Redis. I wanted maximum portability so other developers don't have to add STS if they don't want to. You can add this to any Spring Boot application to offer clean shutdown. Here's what I did, elaborating on the OP's own answer.
Add this to your main class, or any @Configuration
class:
@Bean
public ApplicationRunner systemExitListener() {
return args -> {
if (args.getOptionValues("exitListener") != null) {
System.out.println("Press Enter to exit application");
new Scanner(System.in).nextLine();
System.out.println("Exiting");
System.exit(0);
}
};
}
Then in Eclipse (or in your IDE of choice, or even on the command line), add the argument --exitListener
to activate the code. In Eclipse, that would be in Run Configurations, on the Arguments tab, in the Program Arguments box.
After investigation, it turns out that Eclipse
simply terminates the application without any chance for a shutdown hook to be run.
I've found a workaround/hack thanks to how Spring-Boot
is wired. When the main method is executed, tomcat is started in another thread and the main method continues to execute until completion. This allowed me to insert a termination logic when you hit 'Enter'.
The application launches normally and the console simply awaits for an enter key to execute System.exit(1);
:
@SpringBootApplication
@EnableRedisRepositories
public class Launcher {
public static void main(String[] args){
new SpringApplicationBuilder() //
.sources(Launcher.class)//
.run(args);
System.out.println("Press 'Enter' to terminate");
new Scanner(System.in).nextLine();
System.out.println("Exiting");
System.exit(1);
}
}
When launching the application from Eclipse, instead of terminating the application from the interface, I now hit enter in the console.
Now, the shutdown hooks (@PreDestroy
) is triggered and the Redis server is stopped!
Not what I hoped for, but at least for the time being the Embedded Redis Server is stopped with the application and I don't have to keep killing it manually.
When using SpringBoot and Eclipse, you can install the STS (Spring Tool Suite) eclipse plugin to achieve graceful shutdown.
Once installed, run the application as a "Spring Boot App" instead of regular "Java application" (Run/debug configurations)
Ensure that the checkbox "Enable Life Cycle Management" checkbox is checked and when you will click on the red square button to stop the application, it will perform a graceful shutdown instead of a hard kill.
Edit:
It's worth noticing that there are two "red square button". One in the "Launch" toolbar and one in the "Console" panel. The one in the launch toolbar still performs hard kill but the one in the console allows the graceful shutdown for spring-boot application (launched with STS)