I have a WAR file deployed to Tomcat server, one of the class will get called at start up time, then the init() method will schedule a timer to fire every 5 hours to perform some tasks.
My init() code looks like this:
public void init()
{
TimerTask parserTimerTask = new TimerTask() {
@Override
public void run() {
XmlParser.parsePage();
}
};
Timer parserTimer = new Timer();
parserTimer.scheduleAtFixedRate(parserTimerTask, 0, PERIOD);
}
My application runs without problem, but when I shutdown the Tomcat using /etc/init.d/tomcat7 stop, then I check the log (catalina.out) it has a entry like this:
SEVERE: The web application [/MyApplication] appears to have started a thread named [Timer-0] but has failed to stop it. This is very likely to create a memory leak.
I understand this is caused by me schedule the timer, but my question is:
setDeamon
to true, so shouldn't the timer prevent Tomcat from shutting down, rather than left running?Thanks!
UPDATE
I changed my code to the following based on some search and DaveHowes's answer.
Timer parserTimer;
TimerTask parserTimerTask;
public void init()
{
parserTimerTask = new TimerTask() {
@Override
public void run() {
XmlParser.parsePage();
}
};
parserTimer = new Timer();
parserTimer.scheduleAtFixedRate(parserTimerTask, 0, PERIOD);
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
Logger logger = Logger.getRootLogger();
logger.info("DETECT TOMCAT SERVER IS GOING TO SHUT DOWN");
logger.info("CANCEL TIMER TASK AND TIMER");
otsParserTimerTask.cancel();
otsParserTimer.cancel();
logger.info("CANCELING COMPLETE");
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
}
Now my new question:
Thanks!
UPDATE
It doesn't work. I put some logging statement in the contextDestroyed() method, after I shutdown Tomcat, the log file only has the following:
PowderGodAppWebService -> [07 Feb 2012 04:09:46 PM] INFO (PowderGodAppWebService.java:45):: DETECT TOMCAT SERVER IS GOING TO SHUT DOWN PowderGodAppWebService -> [07 Feb 2012 04:09:46 PM] INFO (PowderGodAppWebService.java:46):: CANCEL TIMER TASK AND TIMER
CANCELING COMPLETE is not there.
I also checked processes that are running (I'm not a Linux expert so I just use Mac's Activity Monitor.
FIXED
I changed my code to parserTimer = new Timer(true);
so that my timer runs as a daemon thread because the contextDestroyed()
gets called after Tomcat actually shuts down.
"All servlets and filters will have been destroyed before any ServletContextListeners are notified of context destruction."
http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html
The cancel() method is used to cancel the timer task. The cancel() methods returns true when the task is scheduled for one-time execution and has not executed until now and returns false when the task was scheduled for one-time execution and has been executed already.
The purge() method of Timer class in Java is used to remove all the cancelled tasks from this queue of the Timer. The behaviour of the time remains unaffected by the calling of this method. Syntax: public int purge() Parameters: The method does not take any parameters.
The Timer Class in Java is a facility for threads to plan tasks for future execution in a background thread. Tasks may be executed a single time or multiple times. The Timer class is thread-safe i.e. the threads of the class do not require external synchronization and can share a single Timer object.
Do not use Timer
in an Java EE environment! If the task throws a runtime exception, then the whole Timer
is killed and won't run anymore. You basically needs to restart the whole server to get it to run again. Also, it is sensitive to changes in the system clock.
Use ScheduledExecutorService
instead. It's not sensitive to exceptions thrown in tasks nor to changes in system clock. You can shutdown it by its shutdownNow()
method.
Here's an example of how the entire ServletContextListener
implementation can look like (note: no registration in web.xml
required thanks to the new @WebListener
annotation):
@WebListener
public class BackgroundJobManager implements ServletContextListener {
private ScheduledExecutorService scheduler;
@Override
public void contextInitialized(ServletContextEvent event) {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new YourParsingJob(), 0, 5, TimeUnit.HOUR);
}
@Override
public void contextDestroyed(ServletContextEvent event) {
scheduler.shutdownNow();
}
}
The servlets destroy method is called as the servlet is about to be unloaded. You could cancel the timer from within there, providing that you altered the scope of the parserTimer itself to make it an instance variable. I don't see a problem with that provided that you access it only from within init and destroy.
The servlet engine is free to unload the servlet whenever it sees fit, but in pratice I'm only ever seen it called as the servlet engine is stopped - other people may have other experiences of this though that might prove me wrong.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With