Should the Spring Boot shutdown endpoint shut down the entire JVM process, or just the application context? - spring-boot

I am getting a 200 response from Spring Boot's shutdown endpoint, and I am seeing that the application context shuts down as expected, but then the JVM process itself remains alive forever. Is this the expected behavior of the shutdown endpoint, or is it expected that the process itself would also terminate gracefully?
In http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html, it says that the shutdown endpoint "allows the application to be gracefully shutdown (not enabled by default)".

Thanks Stéphane, I found what was preventing the JVM process from terminating after hitting the /shutdown endpoint. There was a ScheduledExecutor in one of my dependencies that was not being shut down with the application context, and it was preventing the JVM process from shutting down (even after the application context was closed). I wrote a simple example to show how to reproduce the behavior, and another example showing how to resolve it.
This example will NOT terminate the JVM process when you hit /shutdown endpoint:
#SpringBootApplication
public class AppSpringConfiguration {
public static void main(String[] args) {
SpringApplication.run(AppSpringConfiguration.class);
}
#Bean
public ClassWithExecutor ce() {
return new ClassWithExecutor();
}
#PostConstruct
public void startScheduledTask() {
ce().startScheduledTask();
}
#RestController
public static class BusinessLogicController {
#RequestMapping(value = "/hi")
public String businessLogic() {
return "hi";
}
}
public static class ClassWithExecutor {
ScheduledExecutorService es;
ClassWithExecutor() {
this.es = Executors.newSingleThreadScheduledExecutor();
}
public void startScheduledTask() {
es.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
System.out.println("Printing this every minute");
}
}, 0, 3, TimeUnit.SECONDS);
}
}
}
By adding a shutdown hook that shuts down the ScheduledExecutor when the application context is closing, the JVM process now gets terminated after hitting the /shutdown endpoint:
#SpringBootApplication
public class AppSpringConfiguration {
public static void main(String[] args) {
SpringApplication.run(AppSpringConfiguration.class);
}
#Bean
public ClassWithExecutor ce() {
return new ClassWithExecutor();
}
#Bean
ShutdownAction sa() {
return new ShutdownAction(ce());
}
#PostConstruct
public void startScheduledTask() {
ce().startScheduledTask();
}
#RestController
public static class BusinessLogicController {
#RequestMapping(value = "/hi")
public String businessLogic() {
return "hi";
}
}
public static class ShutdownAction implements ApplicationListener<ContextClosedEvent> {
private ClassWithExecutor classWithExecutor;
ShutdownAction(ClassWithExecutor classWithExecutor) {
this.classWithExecutor = classWithExecutor;
}
#Override
public void onApplicationEvent(ContextClosedEvent event) {
classWithExecutor.shutdown();
}
}
public static class ClassWithExecutor {
ScheduledExecutorService es;
ClassWithExecutor() {
this.es = Executors.newSingleThreadScheduledExecutor();
}
public void startScheduledTask() {
es.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
System.out.println("Printing this every minute");
}
}, 0, 3, TimeUnit.SECONDS);
}
public void shutdown() {
es.shutdownNow();
}
}
}

You have something that prevents the JVM to exit besides your Spring Boot application. If you don't and you have a sample projet that demonstrates the problem, then please create an issue and we'll have a look.
Instead of using the shutdown endpoint, you can use the spring-boot-maven-plugin as of 1.3 that has a start and stop goals to be used in typical integration tests scenarios.

If you have a scheduled executor running you should specify destroy method:
#Bean(destroyMethod = "shutdown")

Related

Application.restart() return null

I'm trying to restart spring boot application after executing an application.
So I've added a context in the main class:
#SpringBootApplication
public class MainApplication {
private static ConfigurableApplicationContext context;
public static void main(String[] args) { context = SpringApplication.run(MainApplication.class, args); }
public static void restart() {
ApplicationArguments args = context.getBean(ApplicationArguments.class);
Thread thread = new Thread(() -> {
context.close();
context = SpringApplication.run(MainApplication.class, args.getSourceArgs());
});
thread.setDaemon(false);
thread.start();
}
}
To call the restart method, Here is the Service:
#EventListener(ApplicationReadyEvent.class)
#Transactional
public void saveMaths() {
int maths = 0;
List<User> users;
try {
users = userRepository.findByMathsWhereNull();
for (User user : users) {
maths = calculateMaths(user);
user.setMaths(maths);
userRepository.updateUser(maths, user.getId());
}
} finally {
restart();
}
}
public void restart() {
Application.restart();
}
But unfortunatelly, if the principal method works, I've a null pointer exception in the finally restart().
I've instanciated maths and users list just to be sure and once again, this method works. What did I do wrong?

How to ensure Spring Cloud Stream Listener to wait to process messages until Application is fully initialized on Start?

With Spring Cloud Stream Kafka app, how can we ensure that the stream listener waits to process messages until some dependency tasks (reference data population, e.g.) are done? Below app fails to process messages because messages are delivered too early. How can we guarantee this kind of ordering within a Spring Boot App?
#Service
public class ApplicationStartupService implements ApplicationRunner {
private final FooReferenceDataService fooReferenceDataService;
#Override
public void run(ApplicationArguments args) throws Exception {
fooReferenceDataService.loadData();
}
}
#EnableBinding(MyBinding.class)
public class MyFooStreamProcessor {
#Autowired FooService fooService;
#StreamListener("my-input")
public void process(KStream<String, Foo> input) {
input.foreach((k,v)-> {
// !!! this fails to save
// messages are delivered too early before foo reference data got loaded into database
fooService.save(v);
});
}
}
spring-cloud-stream: 2.1.0.RELEASE
spring-boot: 2.1.2.RELEASE
I found this is not available in spring cloud stream as of May 15, 2018.
Kafka - Delay binding until complex service initialisation has completed
Do we have a plan/timeline when this is supported?
In the mean time, I achieved what I wanted by using #Ordered and ApplicationRunner. It's messy but works. Basically, stream listener will wait until other works are done.
#Service
#Order(1)
public class ApplicationStartupService implements ApplicationRunner {
private final FooReferenceDataService fooReferenceDataService;
#Override
public void run(ApplicationArguments args) throws Exception {
fooReferenceDataService.loadData();
}
}
#EnableBinding(MyBinding.class)
#Order(2)
public class MyFooStreamProcessor implements ApplicationRunner {
#Autowired FooService fooService;
private final AtomicBoolean ready = new AtomicBoolean(false);
#StreamListener("my-input")
public void process(KStream<String, Foo> input) {
input.foreach((k,v)-> {
while (ready.get() == false) {
try {
log.info("sleeping for other dependent components to finish initialization");
Thread.sleep(10000);
} catch (InterruptedException e) {
log.info("woke up");
}
}
fooService.save(v);
});
}
#Override
public void run(ApplicationArguments args) throws Exception {
ready.set(true);
}
}

How to use threadPoolExecutor for async spring

I have a requirement to use same thread for multiple request in async spring.
Here is the code which i am using :
Here is the Executor configuration file
#Configuration
#EnableAsync
public class ExecutorConfig {
#Bean(name = "threadPoolExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("threadPoolExecutor-");
executor.initialize();
return executor;
}
}
Here is the service file
public interface QueueService {
public void createQueue();
public void sendData();
}
Here is the Service Implementation
#Service
public class QueueServiceImpl implements QueueService {
#Override
#Async("threadPoolExecutor")
public void createQueue() {
System.out.println(Thread.currentThread().getName());
}
#Override
#Async("threadPoolExecutor")
public void sendData() {
// Need to use the same thread which created the queue
// Thread.currentThread() should be same for both methods
System.out.println(Thread.currentThread().getName());
}
}
Here is the controller class
#RestController
public class Controller {
#Autowired
QueueService queueService;
#RequestMapping(value="/create", method = RequestMethod.GET)
public void createQueue()
{
queueService.createQueue();
}
#RequestMapping(value="/send", method = RequestMethod.GET)
public void sendDataToQueue()
{
queueService.sendData();
}
}
Here is the Spring boot main application
#EnableAsync
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Please let me know how to use #Async in 2 separate request to do the job done by same thread.

Spring boot graceful shutdown mid-transaction

I'm working on a spring-boot service that performs sensitive payment processing, and would like to ensure that any shutdown to the app will be done without interrupting these transactions. Curious on how to best approach this in spring-boot.
I read about adding shutdown hooks to spring-boot, and I was thinking maybe to use a CountDownLatch on the class to check if the thread has completed processing - something like this:
#Service
public class PaymentService {
private CountDownLatch countDownLatch;
private void resetLatch() {
this.countDownLatch = new CountDownLatch(1);
}
public void processPayment() {
this.resetLatch();
// do multi-step processing
this.CountDownLatch.countDown();
}
public void shutdown() {
// blocks until latch is available
this.countDownLatch.await();
}
}
// ---
#SpringBootApplication
public class Application {
public static void main(String[] args) {
// init app and get context
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
// retrieve bean needing special shutdown care
PaymentService paymentService = context.getBean(PaymentService.class);
Runtime.getRuntime().addShutdownHook(new Thread(paymentService::shutdown));
}
}
Constructive feedback is greatly appreciated - thanks.
I ended up using #PreDestroy annotation on the shutdown method:
#Service
public class PaymentService {
private CountDownLatch countDownLatch;
private synchronized void beginTransaction() {
this.countDownLatch = new CountDownLatch(1);
}
private synchronized void endTransaction() {
this.countDownLatch.countDown();
}
public void processPayment() {
try {
this.beginTransaction();
// - - - -
// do multi-step processing
// - - - -
} finally {
this.endTransaction();
}
}
#PreDestroy
public void shutdown() {
// blocks until latch is available
this.countDownLatch.await();
}
}

Spring boot check external service status on boot

I want check some external http service before my Spring Boot is ready.
The url to the external web service are stored in a property file with a #ConfigurationProperties class.
How do this check i tried using a springApplication.addListner() with a ping method. But the property class have not then been initialized.
public class ApplicationStartListener implements ApplicationListener<ApplicationPreparedEvent> {
#Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
String url = AppProp.getURL();
inet = InetAddress.getByName(url );
inet.isReachable(5000)
...
application.yml
tops:
http://service.com
#Component
#ConfigurationProperties("tops")
public class AppProp{
private static String url;
public static String getUrl() {
The easiest way to accomplish this is to implement the ApplicationRunner interface.
From the Spring Boot documentation [1]
If you need to run some specific code once the SpringApplication has started, you can implement the ApplicationRunner or CommandLineRunner interfaces. Both interfaces work in the same way and offer a single run method which will be called just before SpringApplication.run(…​) completes.
[1] https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html#boot-features-command-line-runner
Assuming you have url defined in application.properties:
#SpringBootApplication
public class MyApplication implements ApplicationRunner
{
#Inject
private AppConfig appConfig;
#Inject
private ConfigurableApplicationContext applicationContext;
public static void main(String[] args)
{
SpringApplication.run(MyApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception
{
InetAddress inetAddress = InetAddress.getByName(appConfig.getUrl());
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Component
#ConfigurationProperties
public static class AppConfig
{
private String url;
public String getUrl()
{
return url;
}
public void setUrl(String url)
{
this.url = url;
}
}
}
If you need even more control than this, you can use SpringApplicationRunListener [2]
[2] http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/SpringApplicationRunListener.html
#SpringBootApplication
public class MyApplication implements SpringApplicationRunListener
{
public MyApplication() { }
public MyApplication(SpringApplication springApplication, String[] args) { }
public static void main(String[] args)
{
SpringApplication.run(MyApplication.class, args);
}
#Override
public void started() { }
#Override
public void environmentPrepared(ConfigurableEnvironment environment)
{
// 1st opportunity
InetAddress inetAddress = InetAddress.getByName(environment.getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Override
public void contextPrepared(ConfigurableApplicationContext context)
{
// 2nd opportunity
InetAddress inetAddress = InetAddress.getByName(context.getEnvironment().getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Override
public void contextLoaded(ConfigurableApplicationContext context)
{
// 3rd opportunity
InetAddress inetAddress = InetAddress.getByName(context.getEnvironment().getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Override
public void finished(ConfigurableApplicationContext context, Throwable exception)
{
// 4th opportunity
InetAddress inetAddress = InetAddress.getByName(context.getEnvironment().getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Component
#ConfigurationProperties
public static class AppConfig {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
}
then create META-INF\spring.factories and add
org.springframework.boot.SpringApplicationRunListener=com.foobar.MyApplication

Resources