ESala
ESala

Reputation: 7058

How to shut down a Spring Boot command-line application

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.

Upvotes: 59

Views: 65994

Answers (9)

Rafi
Rafi

Reputation: 489

There is a common pitfall when command line runners are used. When starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

is used (often by simple copy-paste mistake) Spring Boot will start servlet container, that will prevent application stop after command line runner completion. This is why in some cases .close() or System.exit() helps, because it shuts down the container and then the application.

If you don't need container simply use:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

and application will shutdown after command line completion without close/exit calls (assuming that there are no hanging threads as Dave Syer explained in his answer).

Upvotes: 1

Gyan-Lucifer
Gyan-Lucifer

Reputation: 238

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

    }
}

Upvotes: 3

Eric Badiere
Eric Badiere

Reputation: 182

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

Upvotes: 1

Maciej Kaczorowski
Maciej Kaczorowski

Reputation: 51

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).

Upvotes: 0

ACV
ACV

Reputation: 10560

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.

Upvotes: 59

Abel ANEIROS
Abel ANEIROS

Reputation: 6484

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

Upvotes: 28

EliuX
EliuX

Reputation: 12685

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

Upvotes: 5

Quan Vo
Quan Vo

Reputation: 1799

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.

Upvotes: 5

Dave Syer
Dave Syer

Reputation: 58124

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).

Upvotes: 31

Related Questions