Search code examples
javatomcatquartz-scheduler

Schedule Task for Webapp on Tomcat


I've seen the following answer when looking for a way to handle schedule tasks in a self learning web app that I'm building. I'm using the latest version of Quartz and Tomcat 8.0.27 that is installed with Netbeans.

Simple example for Quartz 2.2 and Tomcat 7

XML-less

  • This requires Servet 3.0+ (Tomcat 7+, Glassfish 3+, JBoss AS 7)

You only need two files: TestJob.java from the previous example and the following listener:

As I'm not using Maven and don't want to use XML, I have some questions related to it, as I'm getting an error on running the code starting Tomcat.

I'm trying to run the task every twelve hours, at 08:30 and 20:30

The answer says not to

To avoid conflicts, do not set the default listener in the web.xml

But unless I set the Listener in the web xml of my application it does not run the job.

<listener> 
<listener-class>medsched.slisteners.MedSchedContextListener</listener-class> 
</listener>

Should this be the case, or am I misunderstanding this part of the answer?

The error I get, even using the interval of the highlighted answer, relates to the fact that Tomcat sees the thread running without stopping. It reports the following

18-Nov-2017 15:46:20.347 WARNING [http-nio-8084-exec-3]  org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads 
The web application [medsched] appears to have started a thread named  [DefaultQuartzScheduler_Worker-10] but has failed to stop it. 
This is very likely to create a memory leak. Stack trace of thread:
 java.lang.Object.wait(Native Method)
 org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:568)

Is there additional code I should be adding to prevent the error?

public class MedSchedContextListener extends QuartzInitializerListener{
private final static Logger LOG = LogManager.getLogger(MedSchedContextListener.class);
@Override
public void contextInitialized(ServletContextEvent sce) {
    super.contextInitialized(sce);
    ServletContext ctx = sce.getServletContext();
    StdSchedulerFactory factory = (StdSchedulerFactory) ctx.getAttribute(QUARTZ_FACTORY_KEY);
    try {
        Scheduler scheduler = factory.getScheduler();
        JobDetail jobDetail = JobBuilder.newJob(MedSchedReminder.class).build();
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("simple").withSchedule(
                CronScheduleBuilder.cronSchedule("0 30 8,20 1/1 * ? *")).startNow().build();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    } catch (Exception e) {
        LOG.error("There was an error scheduling the job.", e);
    }
}

}

Edited web.xml section which now stops the error.

<context-param>
     <param-name>quartz:shutdown-on-unload</param-name>
     <param-value>true</param-value>
 </context-param>
 <context-param>
     <param-name>quartz:wait-on-shutdown</param-name>
     <param-value>true</param-value>
 </context-param>

 <listener>
    <listener-class>medsched.slisteners.MedSchedContextListener</listener-class>
</listener>

Solution

  • According to source code of QuartzInitializerListener quartz was being shutdown but the waitOnShutdown flag is set to false, hence Tomcat didn't wait for quartz to finish before it shutdown so Tomcat decided that it had left threads running and complained.

    Try to init the quartz:wait-on-shutdown to true in your listener configuration in addition to shutdown-on-unload property which default to true already (web.xml).

     <context-param>
         <param-name>quartz:shutdown-on-unload</param-name>
         <param-value>true</param-value>
     </context-param>
     <context-param>
         <param-name>quartz:wait-on-shutdown</param-name>
         <param-value>true</param-value>
     </context-param>