Reputation: 7058
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
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
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
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
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
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
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
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
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
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