5
votes

I am trying to shutdown threads when Tomcat is being shutdown.
Specifically I am trying to shutdown log4j watchdog (for filechanges) and also I am trying to shutdown an executor which uses a class in my web app.
On shutdown I see exceptions in Catalina.out.
For Log4J I see:

INFO: Illegal access: this web application instance has been stopped
already. Could not load org.apache.log4j.helpers.NullEnumeration.
The eventual following stack trace is caused by an error thrown for
debugging purposes as well as to attempt to terminate the thread which
caused the illegal access, and has no functional impact. Throwable
occurred: java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1587)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1546)
at org.apache.log4j.Category.getAllAppenders(Category.java:413)
at org.apache.log4j.Category.closeNestedAppenders(Category.java:226)
at org.apache.log4j.Hierarchy.shutdown(Hierarchy.java:467)
at org.apache.log4j.LogManager.shutdown(LogManager.java:267)
at com.listeners.myListener$1.run(myListener.java:232)
Exception in thread "Thread-14" java.lang.NoClassDefFoundError:
org.apache.log4j.helpers.NullEnumeration
at org.apache.log4j.Category.getAllAppenders(Category.java:413)
at org.apache.log4j.Category.closeNestedAppenders(Category.java:226)
at org.apache.log4j.Hierarchy.shutdown(Hierarchy.java:467)
at org.apache.log4j.LogManager.shutdown(LogManager.java:267)

And for the executor part:

INFO: Illegal access: this web application instance has been stopped
already. Could not load com.my.class.SomeClass. The eventual
following stack trace is caused by an error thrown for debugging
purposes as well as to attempt to terminate the thread which caused
the illegal access, and has no functional impact. Throwable occurred:
java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1587)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1546)
at Exception in thread "Thread-13" java.lang.NoClassDefFoundError:
com.my.class.SomeClass

What I am doing is in ServletContextListener on contextDestroyed I have added shutdown hooks as follows:

public void contextDestroyed(ServletContextEvent arg0) {  

         Runtime.getRuntime().addShutdownHook(new Thread(){  
            @Override  
            public void run(){  
                LogManager.shutdown();                  
            }  
         });  

    } 





 public void contextDestroyed(ServletContextEvent arg0) {  

        Runtime.getRuntime().addShutdownHook(new Thread(){  
            @Override  
            public void run(){  
                SomeClass.updater.shutdown();  
            }  
        });  

    }  

What am I doing wrong here? Why do I get exceptions?

UPDATE:
SomeClass.updater is a public static ScheduledExecutorService.
LogManager is org.apache.log4j.LogManager

UPDATE2:
After following the answer from BGR I do directly

public void contextDestroyed(ServletContextEvent arg0) {  

            SomeClass.updater.shutdown();  

        }  

and

public void contextDestroyed(ServletContextEvent arg0) {  

                LogManager.shutdown();                  

        } 

I don't get exception from Log4j but I get the following exception for SomeClass.updater which is a public static ScheduledExecutorService:

INFO: Illegal access: this web application instance has been stopped
already. Could not load java.util.concurrent.ExecutorService. The
eventual following stack trace is caused by an error thrown for
debugging purposes as well as to attempt to terminate the thread which
caused the illegal access, and has no functional impact. Throwable
occurred: java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1587)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1546)

Why? Are the classes already been garbage collected?

2

2 Answers

3
votes

I would register shutdown hooks in the init() method of the servlet rather than contextDetroyed(), but anyway, why do you need Shutdown hooks in the first place?

Can't you just call SomeClass.updater.shutdown();directly in the contextDestroyed() method ?

EDIT

contextDestroyed() of the listener is to late for the executor service. As stated in the javadoc All servlets and filters will have been destroyed before any ServletContextListeners are notified of context destruction.

whereas overriding the servlet destroy() should be OK as according to the javadoc This method gives the servlet an opportunity to clean up any resources that are being held (for example, memory, file handles, threads...

@Override
public void destroy(  ) {


        myThreadExecutor.shutdown();

        super.destroy(  );
}
2
votes

Calling

LogManager.shutdown();

in the contextDestroyed() method is the first step but the ExecutorService does not close immediately. You are getting the exceptions because the ExecutorService threads are still running after the contextDestroyed() method returns. You need to do:

public void contextDestroyed(ServletContextEvent arg0) {  
    LogManager.shutdown();
    if(LogManager.awaitTermination(10, TimeUnit.SECONDS) == false) {
        LogManager.shutdownNow();
    }
} 

This way the thread pool has closed and stopped all threads when contextDestroyed() exits.