Spring webapp - shutting down threads on Application stop - java

I am instantiating a ScheduledExecutorService using Spring's ApplicationListener interface as follows:
#Component
public class ExecutorsStart implements ApplicationListener<ContextRefreshedEvent> {
private ScheduledExecutorService executor;
#Autowired
Scheduler scheduler;
#Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
executor = Executors.newSingleThreadScheduledExecutor();
scheduler.init();
int delay = 10;
int period = 60;// repeat every 1 minutes.
executor.scheduleAtFixedRate(scheduler, delay, period, TimeUnit.SECONDS);
}
At the moment, Tomcat won't shut down cleanly when I run, ./shutdown.sh, with message:
The web application [/foo] appears to have started a thread named [pool-1-thread-1] but has failed to stop it
and this seems to be because I have not yet written code to stop the ScheduledExecutorService.
My question is: how should this be done properly in this environment?
I noticed that there exists a ContextStoppedEvent, so, I implemented a listener for it:
#Component
public class ExecutorsStop implements ApplicationListener<ContextStoppedEvent> {
#Autowired
ExecutorsStart executorsStart;
#Override
public void onApplicationEvent(final ContextStoppedEvent event) {
executorsStart.executor.shutdownNow();
}
But it seems that this event handler doesn't get called when Tomcat is shutdown.
Have I implemented this incorrectly, or am I going about this completely the wong way?

You're looking for ContextClosedEvent.
#Component
public class ExecutorsStop implements ApplicationListener<ContextClosedEvent> {
#Autowired
ExecutorsStart executorsStart;
#Override
public void onApplicationEvent(final ContextClosedEvent event) {
System.out.println("Stopped: " + event);
}
}
When the Servlet container shuts down, it calls contextDestroyed(..) on its various ServletContextListener and destroy() on its Servlet instances. The ContextLoaderListener and DispatcherServlet each call close() on their ApplicationContext.

Related

Springboot ThreadPoolTaskScheduler doesnt wait for my tasks to finish before shutting down

I created a SpringBoot 2.4.2 application that has several long running tasks that should not be terminated while they are still running.
I have defined the Scheduler like this:
#Configuration
public class SchedulerConfig implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("my-scheduled-task-pool ");
scheduler.setAwaitTerminationSeconds(30);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.initialize();
taskRegistrar.setTaskScheduler(scheduler);
}
}
Then I have an #Service class in a seperate package that contains a method like this:
#Scheduled(fixedDelay = 5000)
public void myTask() {
someLongRunningTask();
System.out.println("Finished Task")
}
When I terminate the Program by either doing CTRL+C in the Terminal or calling kill -15 {$pid}, spring shuts down before the myTask() method is finished.
Is there a way I can force Spring to always wait for all of my Tasks to finish before Shutting down? I was under the assumption that scheduler.setWaitForTasksToCompleteOnShutdown(true) took care of this.
Thanks a lot for your help

Shutdown spring application from ApplicationListener

Is it possible to close the context from an ApplicationListener?
public class MyListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// on some certain condition we want to shutdown spring ie close the context
((ConfigurableApplicationContext)event.getApplicationContext()).close();
}
}
The problem is that Spring still wants to finish the startup process here:
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
And therefore throws an IllegalStateException:
java.lang.IllegalStateException:
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext#dcb84c98
has been closed already
It seems likely that the question you actually wanted to ask is "How do I interrupt Spring-Boot startup".
Throw an exception from your onApplicationEvent method.
Its seems that you want to interrupt Spring on startup.
Simple you will not able to do that with out getting error.
If you will still like to kill your application while startup just use:
System.exit(0);
But if you want to close the context after the initial you can listen to a different Spring event called ApplicationReadyEvent
#Component
public class MyListener {
#EventListener
public void onApplicationEvent(ApplicationReadyEvent event) throws Exception {
event.getApplicationContext().close();
}
}
ApplicationReadyEvent - Event published as late as conceivably possible to indicate that the application is
* ready to service requests

Call a method after Spring Boot app starts

I have a Java Spring Boot application which has a Scheduler which calls a async task from a Service The task takes a few minutes (usually 3-5mins) to complete.
The same async method from the Service can also be called trough a UI Application, by calling the API from the Spring Boot Controller.
Code:
Scheduler
#Component
public class ScheduledTasks {
#Autowired
private MyService myService;
#Scheduled(cron = "0 0 */1 * * ?")
public void scheduleAsyncTask() {
myService.doAsync();
}
}
Service
#Service
public class MyService {
#Async("threadTaskExecutor")
public void doAsync() {
//Do Stuff
}
}
Controller
#CrossOrigin
#RestController
#RequestMapping("/mysrv")
public class MyController {
#Autowired
private MyService myService;
#CrossOrigin
#RequestMapping(value = "/", method = RequestMethod.POST)
public void postAsyncUpdate() {
myService.doAsync();
}
}
The scheduler runs the async task every hour, but a user can also run it manually from the UI.
But, I do not want the async method to run again if it is already in the middle of execution.
In order to do that, I have created a table in DB which contains a flag which goes on when the method is running and then it is turned off after the method completes.
Something like this in my service class:
#Autowired
private MyDbRepo myDbRepo;
#Async("threadTaskExecutor")
public void doAsync() {
if (!myDbRepo.isRunning()) {
myDbRepo.setIsRunning(true);
//Do Stuff
myDbRepo.setIsRunning(false);
} else {
LOG.info("The Async task is already running");
}
}
Now, the problem is that the flag sometimes gets stuck due to various reasons (app restarting, some other application error etc.)
So, I want to reset the flag in DB each time the spring boot application is deployed and whenever is restarts.
How can I do that? Is there some way to run a method just after the Spring Boot Application starts, from where I can call a method from my Repo to un set the flags in the database?
Check for the #PostConstruct for e.g here https://www.baeldung.com/running-setup-logic-on-startup-in-spring
If you want to do some stuff after whole application booted and ready use below sample from
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
#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;
}
} // class
If it is enough to hook after a single bean creating use #PostConstruct as suggested by #loan M
In your particular case , you need to reset the database after application deployment, so the best way for you to do that is to use the Spring CommandLineRunner .
Spring boot provides a CommanLineRunner interface with a callback run() method which can be invoked at application startup
after the Spring application context is instantiated.
CommandLineRunner beans can be defined within the same application context and can be ordered using the #Ordered interface or #Order
annotation.
#Component
public class CommandLineAppStartupRunnerSample implements CommandLineRunner {
private static final Logger LOG =
LoggerFactory.getLogger(CommandLineAppStartupRunnerSample .class);
#Override
public void run(String...args) throws Exception {
LOG.info("Run method is executed");
//Do something here
}
}
Answer referred from site : https://www.baeldung.com/running-setup-logic-on-startup-in-spring

spring threadpooltaskexecutor causes memory leak in tomcat

I know this question was asked couple of times but none have provided a correct answer so reposting
I have a Spring4-Jersey webservice that runs inside Tomcat 7.
I am using Spring's ThreadPoolTaskExecutor to process some messages off a queue. I have a bean that uses #Scheduled which submits tasks to the executor every 1000 millis.
However, I have noticed when I shutdown Tomcat, it warns me that it can't shutdown some tasks.
SEVERE: The web application appears to have started a thread named [taskExecutor-9] but has failed to stop it. This is very likely to create a memory leak.
org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
this what I have in code to initialize taskExecutor
#Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
http://docs.spring.io/spring/docs/3.2.0.RC1_to_3.2.0.RC2/changes/docdiffs_org.springframework.scheduling.annotation.html
mentions that spring would take care of the threads that i created; but unfortunately it doesn't seem to be case...
Could someone provide any pointers ??
As its a web-application, you can try something like below;
Your SchedulingConfiguration Class
#Configuration
#EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
/* Beans and Other Stuff */
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(workers());
}
#Bean(name = "executorService")
ExecutorService workers() {
return Executors.newScheduledThreadPool(100);
}
}
ShutDown The ExecutorService in ServletContextListener's contextDestroyed method.
#Configuration
public class CustomServletContextListener implements ServletContextListener {
#Autowired
private ExecutorService executorService;
#Override
public void contextInitialized(ServletContextEvent context) {
/* Do stuff If Required */
}
#Override
public void contextDestroyed(ServletContextEvent context) {
executorService.shutdown();
}
}
Worked for me and I use Tomcat8.

ServletContextListener scheduler carry out a task - then stalls on EJB method call to persist into a database in JBOSS 7.1

I have a ServletContextListener that runs a tasks every 20 seconds - That task is to listen on a folder and check for new files and if found import the new file into a database. This is set up using the answer given from this question: Spawning threads in a JSF managed bean for scheduled tasks using a timer
Java Code for ServletContextListener
#WebListener
public class Listener implements ServletContextListener {
private ScheduledExecutorService scheduler;
#Override
public void contextInitialized(ServletContextEvent event) {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new MyTask(), 0,20, TimeUnit.SECONDS);
}
#Override
public void contextDestroyed(ServletContextEvent event) {
scheduler.shutdownNow();
System.out.println("Terminating the Listener");
}
}
Java Code for MyTask
public class MyTask implements Runnable{
#EJB
LoaderManager loaderManager;
#Schedule(hour="0", minute="0", second="0", persistent=false)
public void run() {
//Some unnecessary code left out
if (itIsOkToImportTheDatabase)
{
loaderManager.loadDatabase(pathToNewFile);
if (thisistrue) {
//doesnt matter
} else {
//doesnt matter
}
}
else
{
// not important
}
}
}
}
Java Code for LocalBean to import the data to the database via JPA
#Stateless
#LocalBean
public class LoaderManager implements LoaderManagerDAO{
#Inject
private EntityManager em;
private Row row = null;
private FileInputStream inp;
private Workbook wb;
public void loadDatabase(String FileLocation) {
InputStream inp = null;
// some file set up stuff, using Apache POI
Sheet sheet1 = wb.getSheetAt(0);
for (int i = 1; i <= sheet1.getLastRowNum(); i++) {
// loop through each row
em.persist(elementsInRow);
}
The listener does check the folder successfully - but when the code to import the database is called - it just seems to stall, with no error coming up on the JBOSS console. I have tried to inject the LoaderManager in many ways but have hit a brick wall... The method call loaderManager.loadDatabase(pathToNewFile); does not reach the LoaderManager class (Checked with print statements) unless i use instantiate it like
LoaderManager loaderManager = new LoaderManager():
.. which im pretty sure is wrong, and even with that it only gets as far as (but doesnt execute, checked via print statements)
em.persist(elementsInRow);
None of the rest of the functionality of the WebApp seems to be affected by any of this, Any help/advice would be much appreciated,
Cheers.
Your code makes very little sense. Injections are wrong and use of unmanaged threads is generally a bad idea in Java EE environment. Since you're using a container with Java EE 6, consider using singleton startup bean and timerservice:
#Startup
#Singleton
public class SchedulerBean {
#Resource
private TimerService timerService;
#EJB
private LoaderManager loaderManager;
private Timer timer;
#PostConstruct
public void init() {
timer = timerService.createIntervalTimer(20 * 1000, 20 * 1000, new TimerConfig(null, false));
}
#PreDestroy
public void deinit() {
timer.cancel();
}
#Timeout
public void schedule(Timer timer) {
// TODO: Add your checks here
loaderManager.loadDatabase(databaseLocation);
}
}
Also I think that you should be using a singleton bean instead of stateless bean for LoaderManager.
You are instantiating MyTask in the following line
scheduler.scheduleAtFixedRate(new MyTask(), 0,20, TimeUnit.SECONDS);
Which makes use of
#EJB
LoaderManager loaderManager;
Which will be injected by the EJB Container. This way most probably it won't work.
One way to achieve this is by doing the following by having a Constructor for MyTask
public MyTask() {
try {
InitialContext ctx = new InitialContext();
loaderManager = (LoaderManager) ctx.lookup("<Package Name>.LoaderManager");
} catch (NamingException ex) {
// Error Handling
}
}
In this case if LoaderManager is exposed to other applications as a Remote Client then you should be able to get this back.

Categories

Resources