Programmatically shut down Spring Boot application - java

How can I programmatically shutdown a Spring Boot application without terminating the VM?
In other works, what is the opposite of
new SpringApplication(Main.class).run(args);

Closing a SpringApplication basically means closing the underlying ApplicationContext. The SpringApplication#run(String...) method gives you that ApplicationContext as a ConfigurableApplicationContext. You can then close() it yourself.
For example,
#SpringBootApplication
public class Example {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
// ...determine it's time to shut down...
ctx.close();
}
}
Alternatively, you can use the static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...) helper method to do it for you. For example,
#SpringBootApplication
public class Example {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
// ...determine it's time to stop...
int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
#Override
public int getExitCode() {
// no errors
return 0;
}
});
// or shortened to
// int exitCode = SpringApplication.exit(ctx, () -> 0);
System.exit(exitCode);
}
}

The simplest way would be to inject the following object where you need to initiate the shutdown
ShutdownManager.java
import org.springframework.context.ApplicationContext;
import org.springframework.boot.SpringApplication;
#Component
class ShutdownManager {
#Autowired
private ApplicationContext appContext;
/*
* Invoke with `0` to indicate no error or different code to indicate
* abnormal exit. es: shutdownManager.initiateShutdown(0);
**/
public void initiateShutdown(int returnCode){
SpringApplication.exit(appContext, () -> returnCode);
}
}

This works, even done is printed.
SpringApplication.run(MyApplication.class, args).close();
System.out.println("done");
So adding .close() after run()
Explanation:
public ConfigurableApplicationContext run(String... args)
Run the Spring application, creating and refreshing a new
ApplicationContext. Parameters:
args - the application arguments (usually passed from a Java main
method)
Returns:
a running ApplicationContext
and:
void close() Close this application context, releasing all resources
and locks that the implementation might hold. This includes destroying
all cached singleton beans. Note: Does not invoke close on a parent
context; parent contexts have their own, independent lifecycle.
This method can be called multiple times without side effects:
Subsequent close calls on an already closed context will be ignored.
So basically, it will not close the parent context, that's why the VM doesn't quit.

This will make sure that the SpringBoot application is closed properly and the resources are released back to the operating system,
#Autowired
private ApplicationContext context;
#GetMapping("/shutdown-app")
public void shutdownApp() {
int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
System.exit(exitCode);
}

In the application you can use SpringApplication. This has a static exit() method that takes two arguments: the ApplicationContext and an ExitCodeGenerator:
i.e. you can declare this method:
#Autowired
public void shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) {
SpringApplication.exit(applicationContext, exitCodeGenerator);
}
Inside the Integration tests you can achieved it by adding #DirtiesContext annotation at class level:
#DirtiesContext(classMode=ClassMode.AFTER_CLASS) - The associated ApplicationContext will be marked as dirty after the test class.
#DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - The associated ApplicationContext will be marked as dirty after each test method in the class.
i.e.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {Application.class},
webEnvironment= SpringBootTest.WebEnvironment.DEFINED_PORT, properties = {"server.port:0"})
#DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ApplicationIT {
...

Related

Best way to call java interface methods after springboot application start up? [duplicate]

I want to run code after my spring-boot app starts to monitor a directory for changes.
I have tried running a new thread but the #Autowired services have not been set at that point.
I have been able to find ApplicationPreparedEvent, which fires before the #Autowired annotations are set. Ideally I would like the event to fire once the application is ready to process http requests.
Is there a better event to use, or a better way of running code after the application is live in spring-boot?
It is as simple as this:
#EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
System.out.println("hello world, I have just started up");
}
Tested on version 1.5.1.RELEASE
Try:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
#SuppressWarnings("resource")
public static void main(final String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
context.getBean(Table.class).fillWithTestdata(); // <-- here
}
}
Have you tried ApplicationReadyEvent?
#Component
public class ApplicationStartup
implements ApplicationListener<ApplicationReadyEvent> {
/**
* This event is executed as late as conceivably possible to indicate that
* the application is ready to service requests.
*/
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
// here your code ...
return;
}
}
Code from: http://blog.netgloo.com/2014/11/13/run-code-at-spring-boot-startup/
This is what the documentation mentions about the startup events:
...
Application events are sent in the following order, as your application runs:
An ApplicationStartedEvent is sent at the start of a run, but before
any processing except the registration of listeners and initializers.
An ApplicationEnvironmentPreparedEvent is sent when the Environment to be used in the context is known, but before the context
is created.
An ApplicationPreparedEvent is sent just before the refresh is started, but after bean definitions have been loaded.
An ApplicationReadyEvent is sent after the refresh and any related callbacks have been processed to indicate the application is ready to
service requests.
An ApplicationFailedEvent is sent if there is an exception on startup.
...
Why not just create a bean that starts your monitor on initialization, something like:
#Component
public class Monitor {
#Autowired private SomeService service
#PostConstruct
public void init(){
// start your monitoring in here
}
}
the init method will not be called until any autowiring is done for the bean.
The "Spring Boot" way is to use a CommandLineRunner. Just add beans of that type and you are good to go. In Spring 4.1 (Boot 1.2) there is also a SmartInitializingBean which gets a callback after everything has initialized. And there is SmartLifecycle (from Spring 3).
ApplicationReadyEvent is really only useful if the task you want to perform is not a requirement for correct server operation. Starting an async task to monitor something for changes is a good example.
If, however your server is in a 'not ready' state until the task is completed then it's better to implement SmartInitializingSingleton because you'll get the callback before your REST port has been opened and your server is open for business.
Don't be tempted to use #PostConstruct for tasks that should only happen once ever. You'll get a rude surprise when you notice it being called multiple times...
You can extend a class using ApplicationRunner , override the run() method and add the code there.
import org.springframework.boot.ApplicationRunner;
#Component
public class ServerInitializer implements ApplicationRunner {
#Override
public void run(ApplicationArguments applicationArguments) throws Exception {
//code goes here
}
}
Use a SmartInitializingSingleton bean in spring > 4.1
#Bean
public SmartInitializingSingleton importProcessor() {
return () -> {
doStuff();
};
}
As alternative a CommandLineRunner bean can be implemented or annotating a bean method with #PostConstruct.
Best way to execute block of code after Spring Boot application started is using PostConstruct annotation.Or also you can use command line runner for the same.
1. Using PostConstruct annotation
#Configuration
public class InitialDataConfiguration {
#PostConstruct
public void postConstruct() {
System.out.println("Started after Spring boot application !");
}
}
2. Using command line runner bean
#Configuration
public class InitialDataConfiguration {
#Bean
CommandLineRunner runner() {
return args -> {
System.out.println("CommandLineRunner running in the UnsplashApplication class...");
};
}
}
Providing an example for Dave Syer answer, which worked like a charm:
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(CommandLineAppStartupRunner.class);
#Override
public void run(String...args) throws Exception {
logger.info("Application started with command-line arguments: {} . \n To kill this application, press Ctrl + C.", Arrays.toString(args));
}
}
I really like the suggestion for usage of the EventListener annotation by #cahen (https://stackoverflow.com/a/44923402/9122660) since it is very clean. Unfortunately I could not get this to work in a Spring + Kotlin setup. What does work for Kotlin is adding the class as a method parameter:
#EventListener
fun doSomethingAfterStartup(event: ApplicationReadyEvent) {
System.out.println("hello world, I have just started up");
}
You have several choices:
Using CommandLineRunner or ApplicationRunner as a Bean definition:
Spring Boot executes these towards the end of the application startup process. In most circumstances, the CommandLineRunner will do the job. Following is an example of a CommandLineRunner implementation with Java 8:
#Bean
public CommandLineRunner commandLineRunner() {
return (args) -> System.out.println("Hello World");
}
Note that the args is the String array of arguments. You can also provide an implementation of this interface and define it as a Spring Component:
#Component
public class MyCommandLineRunner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
System.out.println("Hello World");
}
}
You can use the ApplicationRunner if you need better argument management. ApplicationRunner takes an ApplicationArguments instance that has enhanced argument management options.
You can also order the CommandLineRunner and ApplicationRunner beans using Spring's #Order annotation:
#Bean
#Order(1)
public CommandLineRunner commandLineRunner() {
return (args) -> System.out.println("Hello World, Order 1");
}
#Bean
#Order(2)
public CommandLineRunner commandLineRunner() {
return (args) -> System.out.println("Hello World, Order 2");
}
Using Spring Boot's ContextRefreshedEvent:
Spring Boot publishes several events at startup. These events indicate the completion of a phase in the application startup process. You can listen to the ContextRefreshedEvent and execute custom code:
#EventListener(ContextRefreshedEvent.class)
public void execute() {
if(alreadyDone) {
return;
}
System.out.println("hello world");
}
ContextRefreshedEvent is published several times. Thus, ensure to put a check whether the code execution is already finished.
Try this one and it will run your code when the application context has fully started.
#Component
public class OnStartServer implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent arg0) {
// EXECUTE YOUR CODE HERE
}
}
Spring boot provides an ApplicationRunner interface with a run() method to be invoked at application startup.
However, instead of raw String arguments passed to the callback method, we have an instance of the ApplicationArguments class.
#Component
public class AppStartupRunner implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) throws Exception {
//some logic here
}
}
just implement CommandLineRunner for spring boot application.
You need to implement run method,
public classs SpringBootApplication implements CommandLineRunner{
#Override
public void run(String... arg0) throws Exception {
// write your logic here
}
}
you can use #Component
#RequiredArgsConstructor
#Component
#Slf4j
public class BeerLoader implements CommandLineRunner {
//declare
#Override
public void run(String... args) throws Exception {
//some code here
}
If you mean running peace of code once after the application started, you can use the CommandLineRunner as below:
#SpringBootApplication
public class SpringBootApplication
implements CommandLineRunner {
private static Logger LOG = LoggerFactory
.getLogger(SpringBootConsoleApplication.class);
public static void main(String[] args) {
LOG.info("STARTING THE APPLICATION");
SpringApplication.run(SpringBootConsoleApplication.class, args);
LOG.info("APPLICATION FINISHED");
}
#Override
public void run(String... args) {
// enter code you want to run after app loaded here
LOG.info("EXECUTING : command line runner");
for (int i = 0; i < args.length; ++i) {
LOG.info("args[{}]: {}", i, args[i]);
}
}
}
Otherwise, you can use the DevTools dependency, which help you to run new codes without manually restarting the application.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
don't forget to add these codes to your pom.xml to avoid version warnings:
<properties>
<java.version>17</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
give it a thump up if this was helpful to you!
Best way you use CommandLineRunner or ApplicationRunner
The only difference between is run() method
CommandLineRunner accepts array of string and ApplicationRunner accepts ApplicationArugument.

Spring-boot stop application when internal error occurs [duplicate]

How can I programmatically shutdown a Spring Boot application without terminating the VM?
In other works, what is the opposite of
new SpringApplication(Main.class).run(args);
Closing a SpringApplication basically means closing the underlying ApplicationContext. The SpringApplication#run(String...) method gives you that ApplicationContext as a ConfigurableApplicationContext. You can then close() it yourself.
For example,
#SpringBootApplication
public class Example {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
// ...determine it's time to shut down...
ctx.close();
}
}
Alternatively, you can use the static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...) helper method to do it for you. For example,
#SpringBootApplication
public class Example {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
// ...determine it's time to stop...
int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
#Override
public int getExitCode() {
// no errors
return 0;
}
});
// or shortened to
// int exitCode = SpringApplication.exit(ctx, () -> 0);
System.exit(exitCode);
}
}
The simplest way would be to inject the following object where you need to initiate the shutdown
ShutdownManager.java
import org.springframework.context.ApplicationContext;
import org.springframework.boot.SpringApplication;
#Component
class ShutdownManager {
#Autowired
private ApplicationContext appContext;
/*
* Invoke with `0` to indicate no error or different code to indicate
* abnormal exit. es: shutdownManager.initiateShutdown(0);
**/
public void initiateShutdown(int returnCode){
SpringApplication.exit(appContext, () -> returnCode);
}
}
This works, even done is printed.
SpringApplication.run(MyApplication.class, args).close();
System.out.println("done");
So adding .close() after run()
Explanation:
public ConfigurableApplicationContext run(String... args)
Run the Spring application, creating and refreshing a new
ApplicationContext. Parameters:
args - the application arguments (usually passed from a Java main
method)
Returns:
a running ApplicationContext
and:
void close() Close this application context, releasing all resources
and locks that the implementation might hold. This includes destroying
all cached singleton beans. Note: Does not invoke close on a parent
context; parent contexts have their own, independent lifecycle.
This method can be called multiple times without side effects:
Subsequent close calls on an already closed context will be ignored.
So basically, it will not close the parent context, that's why the VM doesn't quit.
This will make sure that the SpringBoot application is closed properly and the resources are released back to the operating system,
#Autowired
private ApplicationContext context;
#GetMapping("/shutdown-app")
public void shutdownApp() {
int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
System.exit(exitCode);
}
In the application you can use SpringApplication. This has a static exit() method that takes two arguments: the ApplicationContext and an ExitCodeGenerator:
i.e. you can declare this method:
#Autowired
public void shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) {
SpringApplication.exit(applicationContext, exitCodeGenerator);
}
Inside the Integration tests you can achieved it by adding #DirtiesContext annotation at class level:
#DirtiesContext(classMode=ClassMode.AFTER_CLASS) - The associated ApplicationContext will be marked as dirty after the test class.
#DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - The associated ApplicationContext will be marked as dirty after each test method in the class.
i.e.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {Application.class},
webEnvironment= SpringBootTest.WebEnvironment.DEFINED_PORT, properties = {"server.port:0"})
#DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ApplicationIT {
...

Guice Injector keeps application running

I am using guice to inject my dependencies. My IDE is IntelliJ 2017.2.5. When I run the following code my main method keeps on running and is not stopped. When I remove the DI the process stops with Process finished with exit code 0.
The class with the main method:
public class Test {
#Inject
Provider<EntityManager> em;
public void test() {
if(em.get().isOpen())
System.out.println("EM open");
}
public static void main(String args[]) {
final Injector injector = createInjector(new DatabaseModule());
Test app = injector.getInstance(Test.class);
app.test();
System.out.println("Done");
}
}
The DatabaseModule:
public class DatabaseModule extends AbstractModule {
private static final String PU_NAME = "my_pu";
#Override
protected void configure() {
install(new JpaPersistModule(PU_NAME));
bind(JPAInitializer.class).asEagerSingleton();
}
#Singleton
private static class JPAInitializer {
#Inject
public JPAInitializer(final PersistService service) {
service.start();
}
}
}
If I execute Test.main all goes well, JPA is initialised properly and I see the following output:
EM open
Done
For some reason the application is still running after that. I have to terminate the application manually.
How do I fix this?
You're not releasing acquired resources (DB connections and non-daemon threads).
public class Test {
#Inject
Provider<EntityManager> em;
#Inject
UnitOfWork unitOfWork;
public void test() {
if(em.get().isOpen())
System.out.println("EM open");
unitOfWork.end(); // releases DB connection
}
public static void main(String args[]) {
final Injector injector = createInjector(new DatabaseModule());
Test app = injector.getInstance(Test.class);
app.test();
System.out.println("Done");
injector.get(PersistService.class).stop(); // releases resources acquired by the underlying EntityManagerFactory
}
}
Technically just stoping PersistService should be enough in this case, but in general you should close EntityManager when it's not needed anymore. The way you're supposed to do this with Guice JPA is either by using #Transactional annotation or manually ending corresponding unit of work (as in the example above).
You probably have some open DB connections/threads waiting to be explicitly closed
Since you're using Hibernate make sure you close() the SessionFactory at the end (if that's what you're using)
In IntelliJ you can get the thread dump to see which threads are still running/waiting to be closed
Check that to see what you missed

Spring boot test - wait for termination

I have an application written with Spring boot and I am trying to write some integration tests for it. I want to run my spring boot application and wait until it terminates so I can assert some state.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(MyApp.class)
#IntegrationTest("initParams")
public class Test {
#Test
public void test() {
// need to wait until termination
}
Is there any way I can wait for the termination of the Spring app without modifying the application code?
If you the test needs the app to terminates, it would be better to actually run the app as part of the test rather than letting the test context starts it for you. I am not sure why you need it and I wouldn't recommend it for all use cases but this could work:
public class Test {
#Test
public void test() {
String[] args = new String[]{"initParams"}; // To adapt
ConfigurableApplicationContext ctx = SpringApplication.run(MyApp.class, args);
// whatever
ctx.close();
// whatever after the app has terminated.
}
}
I'm not sure if that is a good idea, but here it is: You could perform some asserts in an #After and #AfterClass methods ?
#After
public void afterMethods() {
//asserts
}
#AfterClass
public static void afterClass() {
//asserts
}

Java Spring application #autowired returns null pointer exception

Im fairly new to Java Spring IoC and here's my problem
I have a FactoryConfig class with all beans and annotation #Configuration and #ComponentScan written as below.
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
//And few more #Bean's
}
My Test class has a simple Print method
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
Now in my Main Class Ive created an ApplicationContentext of FactoryConfig. (I'm expecting all of my #Beans in Factory config will be initialised. However, it returns null when I access the Test class using #Autowired
My Main Class
public class Main {
#Autowired
protected static Test _autoTest;
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
// _autoTest.Print(); <--- Im getting NULL Pointer Ex here
}
}
What is the correct way to #Autowire and use objects/beans? any clearer explanation would be much appreciated.
Only beans managed by Spring can have #Autowire annotations. Your main class is not managed by Spring: it's created by you and not declared in a Spring context: Spring doesn't known anything about your class, and doesn't inject this property.
You can just access in your main method the Test bean with :
context.getBean(Test.class).Print();
Usually, you get a "bootstrap" from the context, and call this bootstrap to start your application.
Moreover:
On Java, a method shouldn't start with an uppercase. Your Test class should have a print method, not Print.
If you start with Spring, you should maybe try Spring Boot
Spring does not manage your Main class, that's why you are getting Nullpointer Exception.
Using ApplicationContext to load beans, you can get your beans and access Methods as you are already doing -
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
remove the static argument
protected Test _autoTest;
Your class
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
is not visible to Spring. Try adding an appropriate annotation to it, like #Component.
The reason is that your Main is not managed by Spring. Add it as bean in your configuration:
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
#Bean
public Main main(){
return new Main();
}
//And few more #Bean's
}
And then you can edit your main() as follows:
public class Main {
#Autowired
protected Test _autoTest;
public static void main(String[] args) throws InterruptedException {
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
Test test = context.getBean(Test.class);
Main main = context.getBean(Main.class);
test.Print();
main._autoTest.Print();
}
}

Categories

Resources