55

I am building a Command Line java application using Spring Boot to get it working quickly.

The application loads different types of files (for example CSV) and loads them into a Cassandra Database. It does NOT use any web components, it is not a web application.

The problem I am having is to stop the application when the work is done. I am using the Spring CommandLineRunner interface with a @Component to run the tasks, as shown below, but when the work is completed the application does not stop, it keeps running for some reason and I can't find a way to stop it.

@Component
public class OneTimeRunner implements CommandLineRunner {

    @Autowired
    private CassandraOperations cassandra;

    @Autowired
    private ConfigurableApplicationContext context;

    @Override
    public void run(String... args) throws Exception {
        // do some work here and then quit
        context.close();
    }
}

UPDATE: the problem seems to be spring-cassandra, since there is nothing else in the project. Does anyone know why it keeps threads running in the background that prevent the application from stopping?

UPDATE: the problem disappeared by updating to the latest spring boot version.

ESala
  • 6,878
  • 4
  • 34
  • 55
  • Any solution to this? It happens with normal spring boot (without cassandra) – ACV Aug 31 '15 at 20:45
  • 1
    @ACV nope, I just live with it. I learnt that some spring modules spawn threads of their own, e.g. Cassandra has a thread pool for connections, or RabbitMQ when you use listener containers. These take some time to shutdown after you call `context.close()` to stop the application. – ESala Sep 01 '15 at 19:56

8 Answers8

53

I found a solution. You can use this:

public static void main(String[] args) {
    SpringApplication.run(RsscollectorApplication.class, args).close();
    System.out.println("done");
}

Just use .close() on run.

ACV
  • 9,964
  • 5
  • 76
  • 81
  • well yeah.... if you look at the code on the question, I am doing just that. `SpringApplication.run(Main.class, args)` returns a `ConfigurableApplicationContext` which you can store or `@Autowire` later as I do. The problem I am having on this question is that after calling `close()` on the context, the application stalls for some time. – ESala Sep 01 '15 at 20:49
  • OK, clear. Could be that the files take time to be flushed into the database – ACV Sep 02 '15 at 07:50
28

The answer depends on what it is that is still doing work. You can probably find out with a thread dump (eg using jstack). But if it is anything that was started by Spring you should be able to use ConfigurableApplicationContext.close() to stop the app in your main() method (or in the CommandLineRunner).

Dave Syer
  • 56,583
  • 10
  • 155
  • 143
  • Ok, I debugged the application with a breakpoint and there are 17: Thread[New I/O worker#(1-17)]. I'm gonna have to investigate about that. About the ConfigurableApplicationContext.close(), it takes about 30 seconds to stop the application, is that normal?? – ESala Oct 12 '14 at 21:50
  • It's going to have to wait for whatever is working to release the threads. Sounds like a timeout to me. – Dave Syer Oct 13 '14 at 06:53
  • Exactly, I suspect the Spring Cassandra is keeping these threads running for some reason. – ESala Oct 13 '14 at 11:26
  • 1
    is closing the application using ConfigurableApplicationContext.close() a good way to shutdown according to current standards? – KCK May 14 '19 at 12:09
  • Not sure what you mean. It is the only way to close an application context programmatically. – Dave Syer May 14 '19 at 14:48
26

This is a combination of @EliuX answer with @Quan Vo one. Thank you both!

The main different is I pass the SpringApplication.exit(context) response code as a parameter to the System.exit() so if there is an error closing the Spring context you will notice.

The SpringApplication.exit() will close the Spring context.

The System.exit() will close the application.

@Component
public class OneTimeRunner implements CommandLineRunner {

    @Autowired
    private ConfigurableApplicationContext context;

    @Override
    public void run(String... args) throws Exception { 
       System.exit(SpringApplication.exit(context));
    }
}
Abel ANEIROS
  • 6,029
  • 2
  • 25
  • 19
6

I also met this issue in my current project (spring boot application). My solution is:

// releasing all resources
((ConfigurableApplicationContext) ctx).close();
// Close application
System.exit(0);

context.close() don't stop our console application, it is just releasing the resources.

Quan Vo
  • 1,639
  • 2
  • 13
  • 28
4

Use org.springframework.boot.SpringApplication#exit. E.g.

@Component
public class OneTimeRunner implements CommandLineRunner {

    @Autowired
    private ConfigurableApplicationContext context;

    @Override
    public void run(String... args) throws Exception { 
        SpringApplication.exit(context);
    }
}
EliuX
  • 11,389
  • 6
  • 45
  • 40
1

A command line application should return an exit code when shutting down. This post describes using the ExitCodeGenerator and how to use it in the spring application. This approach worked for me:

@SpringBootApplication public class ExitCodeGeneratorDemoApplication implements ExitCodeGenerator {

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

@Override
public int getExitCode() {
    return 42;
}

}

https://www.baeldung.com/spring-boot-exit-codes

Eric Badiere
  • 182
  • 1
  • 4
0

When there is no hanging threads application stops automatically without exit(). I had the same issue and finally I found the reason why it's happening. I always rely on spring mechanism, which closes all Closeable beans when application exits, but it's invoked on JVM shutdown hook, so it'll never be invoked until some non-daemon threads are running. I solved the problem by closing all beans with open threads manually in CommandLineRunner. Unfortunately it's also required for all beans created by auto configurations (e.g. spring-data-elasticsearch creates such beans).

0

I have developed a command line spring application (Spring version 2.7) that stops after executing business logic. All I needed was following application configuration that tells spring that this is not a web application.

Configuration

spring:
  main:
    web-application-type: none

Spring main class

@SpringBootApplication
public class DataInjectionApplication implements CommandLineRunner {

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


    @Override
    public void run(String... args) {
        
        //execute business logic here

    }
}
The_Java_Guy
  • 184
  • 8