0

Im my spring boot application I have a component with a method that run some job in the infinite loop below, actually it checks some data in db:

while(true) {// DOES SOME JOB}

here is my app entry point for spring boot app:

@SpringBootApplication
public class Application implements CommandLineRunner {

    private final Service service;

    @Autowired
    public Application(Service service) {
        this.service = service;
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) {
        service.run();
    }
}

and I enabled actuator's shutdown endpoint, so I would kill the app via:curl -X POST localhost:8080/actuator/shutdown and in my case it kills only spring context but the loop still run... Is it possible to kill the whole app like System.exit(0) does (even with infinite loop).

NOTE: I know it's possible to write my own context aware endpoint with shutdown link and whenever some one request the endpoint I can close the spring context and Syste.exit(0) (it will definitely terminate the llop) and even provide boolean flag to the infinite loop, but does the spring provide something by default, let's say more elegant?

Tymur Berezhnoi
  • 706
  • 1
  • 14
  • 27

2 Answers2

1

You can gracefully shutdown your application using @PreDestroy annotation

@PreDestroy
public void destroy() {
 System.out.println("destroy");
}

So, when you use ctrl+C to kill your application, the loop will also be killed.

Shruti Gupta
  • 310
  • 4
  • 9
  • I can't use ctrl+C cause there several running instances of the app on different ports and I need to send a http request to kill it... @PreDestroy is good enough in conjunction with System.exit(0) but I would avoid using it for now, at least until I have a better solution – Tymur Berezhnoi Jul 18 '19 at 10:53
1

As for the actuator, its "shutdown" method closes the application context.

Although the bean is managed by spring but since there is an infinite loop in some of its methods, spring can't really know this and can't really break this loop.

Spring does manage the life-cycle of the beans and indeed like @Shruti Gupta has stated, you can create a method annotated with @PreDestroy annotation so that spring will call it, but then again, you must implement the logic of breaking the loop.

Here is an example of something that might work for you:

@Component
public class MyBean {
    private boolean shouldKeepCheckingDB = true; // consider volatile as well if you need it, kind of out of scope for this question, but might be useful

    public void checkDB() {
        while(shouldKeepCheckingDB) { // instead of while(true)
            // check the database
        }
    }

    @PreDestroy
    void stop() {
        this.shouldKeepCheckingDB = false;
    } 
}
Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • Yes, this will work, but unfortunately I found that inside the loop I have long read SQL queries to the db, I mean I have a list of tables I have to SELECT, and I iterate over the list, read a lot of tables in nested loop (oops - legacy).... so such approach will require to put the boolean flag over several places (where I have loops), but I would like to have one place to terminate the app... – Tymur Berezhnoi Jul 18 '19 at 11:11
  • well, legacy - I feel your pain :) but I don't think that without some kind of condition whether to proceed or stop it will work, sorry. Even refactoring to thread and stopping it from outside considered a bad practice in Java. – Mark Bramnik Jul 18 '19 at 11:37