Explain Codes LogoExplain Codes Logo

Programmatically shut down Spring Boot application

java
application-context
exit-code-generator
application-shutdown-manager
Nikita BarsukovbyNikita Barsukov·Oct 25, 2024
TLDR

To promptly stop a Spring Boot application, employ the SpringApplication.exit in conjunction with the ApplicationContext. Here's the short and sweet code:

@Autowired private ApplicationContext applicationContext; public void shutdown() { int exitCode = SpringApplication.exit(applicationContext, () -> 0); System.exit(exitCode); }

Execute shutdown() when it's time to bid your Spring Boot app goodbye and it will conclude operations in a well-behaved manner.

Eyelid drooping, caffeine depleting? Exit with style!

When you need to pull the plug but also pass a specific exit status to your operating system, implement the ExitCodeGenerator interface like so:

public class CustomExitCode implements ExitCodeGenerator { @Override public int getExitCode() { // Define custom logic and return the meaning of life, the universe, and everything: return 42; // Hitchhiker's Guide to the Galaxy reference. } } @Autowired private ApplicationContext context; public void shutdownWithCustomCode() { int exitCode = SpringApplication.exit(context, new CustomExitCode()); System.exit(exitCode); }

Trigger shutdownWithCustomCode() and not only will the app end its life cycle, but it will also return a meaningful exit code of 42 to your operating system. Universe, here we come!

Master your ApplicationContext before it masters you

If controlling the lifecycle of the application context gives you a dopamine hit, you'll be enamoured by the ConfigurableApplicationContext:

ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args); // When you're all out of tricks... context.close(); // This takes care of resource cleanup without flicking the JVM's off switch.

Feel free to slam that close() button multiple times; it's forgiving – if the context is already closed, additional calls are no-ops. But hey, who are we to judge?

Managing shutdowns the Godfather way

For those of you who see yourselves as master puppeteers in your sprawling application, you'll want to centralize shutdown logic. Say hello to your new best friend, the ApplicationShutdownManager:

@Component public class ApplicationShutdownManager { @Autowired private ConfigurableApplicationContext context; public int initiateShutdown(int returnCode) { return SpringApplication.exit(context, () -> returnCode); } }

Inject ApplicationShutdownManager where needed, and call initiateShutdown() to make the application an offer it can't refuse.

Advanced directives for the tenacious developer

Remote shutdown: the puppets' strings

Embrace the power of remote shutdown with a specific endpoint. Picture it as your secret backstage entrance to the puppet show:

@RestController public class ShutdownController { @Autowired ApplicationShutdownManager shutdownManager; @PostMapping("/shutdown-app") public void shutdownApp(@RequestParam int returnCode) { shutdownManager.initiateShutdown(returnCode); } }

Now, you're not only in control but also free to command the shutdown from another system or service, all from the comfort of your swivel chair.

Test cleanup: the unglamorous but necessary chore

Keep your integration tests clean by using the @DirtiesContext annotation. This VIP pass helps ensure that the ApplicationContext is closed and removed from the testing cache after execution:

@SpringBootTest @DirtiesContext public class ShutdownIntegrationTest { @Autowired private ApplicationShutdownManager shutdownManager; @Test public void testShutdown() { int returnCode = shutdownManager.initiateShutdown(0); assertEquals(0, returnCode); // The assert check that nobody performs but everyone needs. } }

This way, every test starts with a fresh context, ensuring fidelity to the integral values of clean coding.

Wrangling wild threads to a peaceful exit

If you have asynchronous tasks on the loose, round them up with the ExecutorServiceExitCodeGenerator. It will gracefully wait for completion of currently executing tasks before initiating shutdown:

@Autowired private ExecutorService executorService; @Bean public ExitCodeGenerator executorServiceExitCodeGenerator() { return new ExecutorServiceExitCodeGenerator(executorService); }

Behold, the equivalent of a bedtime story for your background threads, gently putting them to sleep before the shutdown.

Keep calm and let the JVM carry on

Sometimes, you just need to close the ApplicationContext without bullying the JVM into halting. Make it so with the close() method:

context.close(); // The context bows out, but the JVM keeps the beat.

Now, your JVM can freely run any scheduled tasks or processes, unburdened by the departure of your application.