Alex Alex - 2 months ago 89
Java Question

Terminated Spring Boot App in Eclipse - Shutdown hook not called

I have a Spring Boot + Spring Data Redis/KeyValue project. I setup a Spring profile to run the application with all dependencies embedded. So at startup, I launch an embedded Redis Server. Everything works fine when I start it in Eclipse, except that I would like the Redis server to be stopped when I stop the Spring Boot application. So I setup several shutdown hooks, however they are not called when I terminate the application from Eclipse.

They are similar questions on SO, I created this one hoping there would be a Redis solution. Also none of these similar questions are specific to Spring Boot.

I tried many things:


  • Spring Boot's
    ExitCodeGenerator
    ;

  • DisposableBean
    ;

  • @PreDestroy
    ;

  • I tried a ShutdownHook (after @mp911de's answer)



None of them are called.

Perhaps there is a Redis option, timeout, keep alive.. something outside the box I am not aware of ?
How can I ensure that the Redis Server is stopped when my Spring Boot app is abruptly stopped ?

=> I saw this Embedded Redis for spring boot, but
@PreDestroy
is just not called when killing the application unexpectedly.

Here are some of the similar questions:



I also saw this post on eclipse.org discussing how shutdown hook is not called when stopping an application from eclipse: Graceful shutdown of Java Applications




Here's all my relevant code:

Component to start the embedded Redis server at startup (With my attempts to stop it too!!):

@Component
public class EmbeddedRedis implements ExitCodeGenerator, DisposableBean{

@Value("${spring.redis.port}")
private int redisPort;

private RedisServer redisServer;

@PostConstruct
public void startRedis() throws IOException {
redisServer = new RedisServer(redisPort);
redisServer.stop();
redisServer.start();
}

@PreDestroy
public void stopRedis() {
redisServer.stop();
}

@Override
public int getExitCode() {
redisServer.stop();
return 0;
}

@Override
public void destroy() throws Exception {
redisServer.stop();
}
}


application.properties:

spring.redis.port=6379


Spring Boot App:

@SpringBootApplication
@EnableRedisRepositories
public class Launcher {

public static void main(String[] args){
new SpringApplicationBuilder() //
.sources(Launcher.class)//
.run(args);
}

@Bean
public RedisTemplate<String, Model> redisTemplate() {
RedisTemplate<String, Model> redisTemplate = new RedisTemplate<String, Model>();
redisTemplate.setConnectionFactory(jedisConnectionFactory());
return redisTemplate;
}


@Bean
public JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setHostName("localhost");
return jedisConnectionFactory;
}
}


Redis server (~ embedded) running:

$ ps -aef | grep redis
... /var/folders/qg/../T/1472402658070-0/redis-server-2.8.19.app *:6379


The embedded Redis maven dependency:

<dependency>
<groupId>com.github.kstyrc</groupId>
<artifactId>embedded-redis</artifactId>
<version>0.6</version>
</dependency>

Answer

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 (@PreDetroy) 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.

Comments