15

During development a SPRING based scheduler in a tomcat container, I always get this logoutput at undeploy webapp or shutdown server:

Apr 28, 2010 4:21:33 PM org.apache.catalina.core.StandardService stop
INFO: Stopping service Catalina
Apr 28, 2010 4:21:33 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: A web application appears to have started a thread named [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1] but has failed to stop it. This is very likely to create a memory leak.
Apr 28, 2010 4:21:33 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: A web application appears to have started a thread named [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2] but has failed to stop it. This is very likely to create a memory leak.
Apr 28, 2010 4:21:33 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: A web application appears to have started a thread named [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-3] but has failed to stop it. This is very likely to create a memory leak.
Apr 28, 2010 4:21:33 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: A web application appears to have started a thread named [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-4] but has failed to stop it. This is very likely to create a memory leak.
Apr 28, 2010 4:21:33 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: A web application appears to have started a thread named [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-5] but has failed to stop it. This is very likely to create a memory leak.
.
.
.    
SEVERE: A web application created a ThreadLocal with key of type [org.springframework.core.NamedThreadLocal] (value [Prototype beans currently in creation]) and a value of type [null] (value [null]) but failed to remove it when the web application was stopped. To prevent a memory leak, the ThreadLocal has been forcibly removed.
Apr 28, 2010 4:21:34 PM org.apache.coyote.http11.Http11Protocol destroy
INFO: Stopping Coyote HTTP/1.1 on http-8606

How can I fix this?

thank you stevedbrown

I add this listener to my webapp

public class ShutDownHook implements ServletContextListener {
    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        BeanFactory bf = (BeanFactory) ContextLoader.getCurrentWebApplicationContext();
        if (bf instanceof ConfigurableApplicationContext) {
            ((ConfigurableApplicationContext)bf).close();
        }
    }

    @Override
    public void contextInitialized(ServletContextEvent arg0) {
    }
}

and my web.xml

<listener>
    <listener-class>pkg.utility.spring.ShutDownHook</listener-class>
</listener>

but the error is still there.

spring config:

<bean id="run" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="concurrent" value="false" />
    <property name="targetObject" ref="scheduler" />
    <property name="targetMethod" value="task" />
</bean>

<bean id="cronTrg" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="run" />
    <property name="cronExpression" value="0/5 * * * * ?" />
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" destroy-method="destroy">
    <property name="triggers">
        <list>
            <ref bean="cronTrg" />
        </list>
    </property>
</bean>
Community
  • 1
  • 1
Alex
  • 4,033
  • 9
  • 37
  • 52

4 Answers4

6

Imho this is an issue of the quartz scheduler. I filed a bug https://jira.terracotta.org/jira/browse/QTZ-192. As a workaround the sleep() solution suggested by Colin Peters works for me. To not trigger the shutdown twice one could also add the sleep to Spring's SchedulerFactoryBean:

import org.quartz.SchedulerException;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

public class SchedulerFactoryBeanWithShutdownDelay extends SchedulerFactoryBean{

  @Override
  public void destroy() throws SchedulerException {
    super.destroy();
    // TODO: Ugly workaround for https://jira.terracotta.org/jira/browse/QTZ-192
    try {
      Thread.sleep( 1000 );
    } catch( InterruptedException e ) {
      throw new RuntimeException( e );
    }
  }
}
StefanR
  • 538
  • 5
  • 14
  • 2
    The quartz issue is fixed with Quartz 2.1. Unfortunately, Spring 3.1 is based on Quartz 1.8 and an update to 2.0 is considered for Spring 3.2 at the earliest. Source: https://jira.springsource.org/browse/SPR-7987 – StefanR Sep 21 '11 at 06:00
  • 1
    Actually Spring 3.1 does support quartz 2.x http://www.infoq.com/news/2011/10/spring-3.1-rc1-release – bmurmistro Sep 27 '12 at 21:11
  • I've upgraded to SpringFramework 4.1.6.RELEASE and Quartz 2.1.7 and still had to do this work around... – TungstenX Jun 11 '15 at 07:31
3

Here is my solution as none of the ones that I found online worked. This is specifically to shutdown the Quartz scheduler with Spring & Tomcat

My explanation is here: http://forum.springsource.org/showthread.php?34672-Quartz-doesn-t-shutdown&p=370060#post370060

Basically what the problem seemed to be is that Quartz doesn't have enough time to cleanly shutdown and the waitForJobsToCompleteOnShutdown argument doesn't seem to help. So I implemented a custom shutdown listener in the webapp, get a reference to the scheduler and shut it down manually. And then wait for 1 second before proceeding.

public class ShutDownHook implements ServletContextListener
{

    @Override
    public void contextDestroyed(ServletContextEvent arg0)
    {
        try
        {
            // Get a reference to the Scheduler and shut it down
            WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
            Scheduler scheduler = (Scheduler) context.getBean("quartzSchedulerFactory");
            scheduler.shutdown(true);

            // Sleep for a bit so that we don't get any errors
            Thread.sleep(1000);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    @Override
    public void contextInitialized(ServletContextEvent arg0)
    {
    }
Collin Peters
  • 4,353
  • 3
  • 29
  • 36
2

You need to add a shutdown hook - see Registering a shutdown hook in Spring 2.5.

In your case, you probably should add a context listener to your webapp that does this (web.xml entry for the listener + implementing class).

Use close, it's easiest.

((YourClass)yourObject).close();
Community
  • 1
  • 1
stevedbrown
  • 8,862
  • 8
  • 43
  • 58
  • I add the listener but nothing happends. – Alex Apr 28 '10 at 19:42
  • 1
    You want ((ConfigurableApplicationContext)bf).close();, not ((ConfigurableApplicationContext)bf).registerShutdownHook(); unless you actually want to register the shutdown hook with your runtime [good for junit] (Runtime.getRuntime().addShutdownHook(new Thread() { public void run(){ if (ctx instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext)ctx).close(); } } }); ) – stevedbrown Apr 28 '10 at 21:18
  • I'm blind which object I have to close, I added the spring configuration and altered my listener class. – Alex Apr 29 '10 at 07:01
0

The only way to ensure that threads are terminated, is to interrupt and join them.

This can by done by implementing org.quartz.InterruptableJob, as described in the answer to the question How to prevent a memory leak in quartz [?].

Community
  • 1
  • 1
Martin
  • 2,347
  • 1
  • 21
  • 21