If there is a non-daemon thread in a Spring Batch application, when the Batch terminates the application never shutsdown, i.e. the shutdown signal never reaches the JVM.
Is this expected behaviour, or does Spring Batch fail to send the signal due to a malfunction?
I attach a very simple application that reproduces the case: https://github.com/ferblaca/SpringBatchDemo
Versions:
Spring boot 2.4.5
Spring Batch 4.3.2
Java 11
This is not related to Spring Batch. Spring Batch does not prevent your JVM from shutting down. As soon as your job is finished, your JVM should terminate, otherwise there is something else preventing it from shutting down. In your case, it is your executor service that you've configured as non-daemon thread.
I took your example and removed everything related to Spring Batch, and the same thing happens:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
#SpringBootApplication
public class DemoBatchApplication {
public static void main(String[] args) {
new SpringApplicationBuilder().sources(com.example.demo.batch.DemoBatchApplication.class)
.web(WebApplicationType.NONE)
.run(args);
}
private final ScheduledExecutorService scheduledExecutorService = Executors
.newSingleThreadScheduledExecutor(new BasicThreadFactory.Builder()
.namingPattern("task-non-daemon-%d")
.daemon(false)
.build());
#PostConstruct
public void init() {
this.scheduledExecutorService.scheduleAtFixedRate(() -> {
System.out.println("Scheduled task non-daemon!!!!");
}, 1L, 1000L, TimeUnit.MILLISECONDS);
}
}
If you run this app, you should see the same behaviour: the scheduledExecutorService will keep running since you set it as a non-daemon thread. If you change the daemon flag to true, the JVM will stop as soon as your job is finished. Please check What is a daemon thread in Java?.
Related
I need to write a piece of code that will run right after quarks is up and running.
Was wondering if such a thing is possible with Quarkus, I tried to do it with a Quarkus main but this only allows me to run the code prior to Quarkus starting.
What is the first module or class that is running when running the below command?
mvn compile quarkus:dev
You can listen to startup and shutdown events, this is the quarkus guide that explains how to do it.
Just quoting this guide:
package org.acme.lifecycle;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;
import org.jboss.logging.Logger;
#ApplicationScoped
public class AppLifecycleBean {
private static final Logger LOGGER = Logger.getLogger("ListenerBean");
void onStart(#Observes StartupEvent ev) {
LOGGER.info("The application is starting...");
}
void onStop(#Observes ShutdownEvent ev) {
LOGGER.info("The application is stopping...");
}
}
I'm trying to use a routing to stop my spring boot application with the code below
with
#GetMapping("/close/")
fun terminate() {
exitProcess(0)
}
but the test server has a different API, so I can't use this code (It's shutdown a whole system)
My question is: how to stop only my spring boot application (replace exitProcess(0))
You can do it using Actuator's shutdown for example.
Find an example here.
Or you can just close your Application Context and that will work.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class SomeRestController {
#Autowired
ApplicationContext applicationContext;
#GetMapping("/close")
public void terminate() {
((AbstractApplicationContext)applicationContext).close();
}
}
I am having a spring batch application and it contains multiple jobs. I need to shutdown the application gracefully when sending the KILL signal from the command line. It should wait until the currently running job completes. (In the mean time, it should not accept new jobs from scheduler).
This is my Spring Boot Application configuration.
#SpringBootApplication
#EnableScheduling
#EnableBatchProcessing
public class SpringBootBatchExampleApplication {
public static void main(String[] args) {
SpringApplicationBuilder app = new SpringApplicationBuilder(SpringBootBatchExampleApplication.class)
.web(WebApplicationType.NONE);
app.registerShutdownHook(true);
app.build().addListeners(new ApplicationPidFileWriter("/shutdown.pid"));
app.run(args);
}
#Bean
TaskSchedulerCustomizer taskSchedulerCustomizer() {
return taskScheduler -> {
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
};
}
#PreDestroy
public void onDestroy() throws Exception{
Thread.sleep(5000);
}
}
Whenever i send the application kill in the middle of job execution, signal it calls the destroy method and waits for 5 seconds.
After it starts to resume the job execution and suddenly terminate the whole process with some exception due to the ApplicationContext is already closed. So the current job cannot be executed further.
Is there any way to hold the application context close/destroy until the pending/currently running jobs get completed?
I wanted to execute a custom logic as soon as the application started and also whenever properties change in Spring Cloud config repo/server. So I have written something like this:
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AppConfiguration implements ApplicationListener<EnvironmentChangeEvent> {
#Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
// Custom logic goes here. It should be executed on both app context load time
// and on any property change time
}
}
This above code was working few months back during application load time and when property change. But this code stopped working recently with, I guess, Spring boot / cloud version updates.
Currently I am using Sprig boot 1.5.10 and Cloud Edgware.SR3
Found a way to run the custom logic to run it on both load time and property change time.
Basically changed to above code to be called upon any event and then inside the overridden methood onApplicationEvent checking only for below event
ContextRefreshedEvent - Event raised when an application context gets initialized or refreshed.
EnvironmentChangeEvent - Event published to signal a change in the environment such as properties in config repo.
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class AppConfiguration implements ApplicationListener<ApplicationEvent> {
#Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof EnvironmentChangeEvent || event instanceof ContextRefreshedEvent) {
// Custom logic goes here. It should be executed on both app context load time
// and on any property change time
}
}
}
Updated
We can also use #EventListener annotation to do the similar thing, which is very simple and easy use. Refer below example:
#Configuration
public class AppConfiguration {
#EventListener({EnvironmentChangeEvent.class, ContextRefreshedEvent.class})
public void onRefresh() {
// Your code goes here...
}
}
I have an spring app which is using tomcat with websockets. I would like to use the DelegatingSecurityContextRunnable to be executed every time when tomcat creates a new thread, i.e. warp the tomcat thread. Does anyone know how this is done. The reason for the question can be found.here
Maybe this can be done with using AOP and some advice?
In Spring boot you can configure a Wrapper by hooking into the Tomcat connector. See this as an example:
#Bean
public EmbeddedServletContainerFactory servletContainerFactory() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
AbstractProtocol protocolHandler = (AbstractProtocol) connector.getProtocolHandler();
TaskQueue taskqueue = new TaskQueue() {
#Override
public boolean offer(Runnable e, long timeout, TimeUnit unit) throws InterruptedException {
return super.offer(new MyRunnable(e), timeout, unit);
}
#Override
public boolean offer(Runnable o) {
return super.offer(new MyRunnable(o));
}
};
TaskThreadFactory tf = new TaskThreadFactory("artur-" + "-exec-", false, 0);
ThreadPoolExecutor e = new ThreadPoolExecutor(10, 10, 1000, TimeUnit.SECONDS, taskqueue);
taskqueue.setParent(e);
protocolHandler.setExecutor(e);
}
});
return factory;
}
And here is my custom Runable (this can be any wrapper, i did not bother implementing exactly yours):
static class MyRunnable implements Runnable {
private Runnable r;
public MyRunnable(Runnable r) {
this.r = r;
}
#Override
public void run() {
System.out.println("Custom runable");
runInner();
}
void runInner() {
r.run();
}
}
And here are my imports:
import java.util.concurrent.TimeUnit;
import org.apache.catalina.connector.Connector;
import org.apache.coyote.AbstractProtocol;
import org.apache.tomcat.util.threads.TaskQueue;
import org.apache.tomcat.util.threads.TaskThreadFactory;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.PropertySource;
What this does:
The Tomcat connector initialises itself. You can set the executor to use, in which case Tomcat will stop creating its own configuration and instead use yours.
By overwriting the offer methods in the queue, you have the chance to wrap your Runnable in any custom Runnable. In my case, for testing, I simply added a Sysout to see that everything is working correctly.
The Threadpool implementation I used is an exact copy of the tomcat default (minus the properties). This way, behaviour stays the same, except that any Runnable is now your delegating wrapper.
When I test that, my console prints:
Custom runable
I hope this is what you were looking for.
I use spring boot, but this is essentially a tomcat issue not a spring issue. You can adapt the solution to your specific scenario.
-- Artur