web-dev-qa-db-fra.com

Application de démarrage Spring terminée dans Eclipse - Le crochet d'arrêt n'est pas appelé

J'ai un projet Spring Boot + Spring Data Redis/KeyValue. J'ai configuré un profil Spring pour exécuter l'application avec toutes les dépendances intégrées. Donc, au démarrage, je lance un serveur Redis intégré. Tout fonctionne correctement lorsque je le lance dans Eclipse, sauf que je souhaite que le serveur Redis soit arrêté lorsque j'arrête l'application Spring Boot. J'ai donc configuré plusieurs crochets d'arrêt, mais ils ne sont pas appelés lorsque je termine l'application depuis Eclipse.

Ce sont des questions similaires sur SO, J'ai créé celui-ci en espérant qu'il y aurait une solution Redis . De plus, aucune de ces questions similaires n'est spécifique à Spring Boot.

J'ai essayé beaucoup de choses:

  • ExitCodeGenerator;
  • DisposableBean;
  • @PreDestroy;
  • J'ai essayé un ShutdownHook (après la réponse de @ mp911de)

Aucun d'entre eux ne s'appelle.

Peut-être existe-t-il une option Redis, un délai d’attente, rester en vie… quelque chose qui sort de l’encadrement et dont je n’ai pas connaissance? Comment puis-je m'assurer que le serveur Redis est arrêté lorsque mon application Spring Boot est brutalement arrêtée ?

=> J'ai vu ceci Redis intégré pour le démarrage du printemps , mais @PreDestroy n'est tout simplement pas appelé lors de la suppression inattendue de l'application.

Voici quelques-unes des questions similaires:

J'ai également vu ce billet sur Eclipse.org expliquant comment arrêter hook n'est pas appelé lors de l'arrêt d'une application à partir d'Eclipse: Arrêt correct d'applications Java


Voici tout mon code pertinent:

Composant permettant de démarrer le serveur intégré Redis au démarrage (avec mes tentatives pour l'arrêter aussi !!):

@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;
    }
}

Le serveur Redis (~ embedded) en cours d'exécution:

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

La dépendance Redis maven intégrée:

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

Lorsque vous utilisez SpringBoot et Eclipse, vous pouvez installer le plug-in Eclipse STS (Spring Tool Suite) pour obtenir un arrêt en douceur.

Une fois installé, exécutez l'application en tant qu '"application Spring Boot" au lieu d'une "application Java" normale (configurations Run/Debug).

Assurez-vous que la case à cocher "Activer la gestion du cycle de vie" est cochée. Lorsque vous cliquerez sur le bouton carré rouge pour arrêter l'application, il procédera à un arrêt progressif au lieu d'un coup fatal.

Modifier:

Il est intéressant de noter qu'il existe deux "bouton carré rouge". Un dans la barre d’outils "Lancer" et un dans le panneau "Console". Celui de la barre d’outils de lancement continue de tuer mais celui de la console permet l’arrêt en douceur de l’application Spring-Boot (lancée avec STS).

9
Ghurdyl

Après enquête, il s'avère que Eclipse ferme simplement l'application sans aucune chance qu'un crochet d'arrêt soit exécuté.

J'ai trouvé une solution de contournement/hack grâce à la façon dont Spring-Boot est câblé. Lorsque la méthode main est exécutée, Tomcat est démarré dans un autre thread et la méthode main continue de s'exécuter jusqu'à la fin. Cela m'a permis d'insérer une logique de terminaison lorsque vous appuyez sur 'Entrée'.

L’application se lance normalement et la console attend simplement qu’une touche entrée exécute 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);
    }
}

Lors du lancement de l'application à partir d'Eclipse, au lieu de terminer l'application à partir de l'interface, je tape maintenant dans la console. Maintenant, les crochets d'arrêt (@PreDestroy) sont déclenchés et le serveur Redis est arrêté!

Ce n’était pas ce que j’espérais, mais au moins pour le moment, le serveur Redis Embedded est arrêté avec l’application et je n’ai pas besoin de le tuer manuellement.

5
alexbt

ExitCodeGenerator nécessite que l'application appelle la méthode exit

System.exit(SpringApplication
             .exit(SpringApplication.run(SampleBatchApplication.class, args)));

Vous pouvez enregistrer en plus un shutdown hook . Les crochets seront appelés

  • lorsque la VM est terminée normalement (System.exit).
  • en réponse à une interruption (Ctrl+C, SIGINT) ou à un signal (SIGHUP, SIGTERM).

Dans certaines circonstances, si la VM ne s’arrête pas correctement (SIGKILL, erreurs internes VM, erreurs dans les méthodes natives), rien ne garantit que des hooks d’arrêt sont appelés ou non.

Le code de votre composant EmbeddedRedis pourrait ressembler à ceci:

@PostConstruct
public void startRedis() throws IOException {

    redisServer = new RedisServer(redisPort);
    redisServer.start();

    Runtime.getRuntime().addShutdownHook(new Thread(){

        @Override
        public void run() {
            redisServer.stop();
        }
    });
}
3
mp911de

J'ai eu le même problème, mais pas avec Redis. Je voulais une portabilité maximale afin que les autres développeurs n'aient pas à ajouter STS s'ils ne le souhaitent pas. Vous pouvez l'ajouter à n'importe quelle application Spring Boot pour offrir un arrêt complet. Voici ce que j'ai fait, en développant la propre réponse du PO.

Ajoutez ceci à votre classe principale, ou à n’importe quelle classe @Configuration:

    @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);
            }
        };
    }

Ensuite, dans Eclipse (ou dans votre IDE de votre choix, ou même sur la ligne de commande), ajoutez l'argument --exitListener pour activer le code. Dans Eclipse, cela se trouverait dans Run Configurations, sous l'onglet Arguments, dans la zone Program Arguments.

1
Ben Ingle