https://bz.apache.org/bugzilla/show_bug.cgi?id=57772

            Bug ID: 57772
           Summary: WebappClassLoader throws a ClassNotFoundError when the
                    Manager is deploying a new WAR
           Product: Tomcat 8
           Version: 8.0.20
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: critical
          Priority: P2
         Component: Manager
          Assignee: dev@tomcat.apache.org
          Reporter: austinjo...@avadynehealth.com

Overview:
When contexts are updated through the Tomcat manager interface,
ServletContextListener.contextDestroyed implementations which need to load
classes throw ClassNotFoundError.

Steps to reproduce:
1. Create an instance of Tomcat 8.0.20
2. In server.xml, configure the Host with unpackWARs="true" autoDeploy="false".
3. Check out the example webapp from
https://github.com/austinjones/ClassNotFoundGenerator
4. Build the testing WAR using the 'distribute' ant task.
5. Deploy the WAR using an HTTP request to this URL:
http://<your-local-tomcat>/manager/text/deploy?
 war=path/to/workspace/ClassNotFoundGenerator/dist/ClassNotFoundGenerator.war
 &config=path/to/workspace/ClassNotFoundGenerator/web/context.xml
 &path=/ClassNotFoundGenerator
 &update=true
6. Open localhost.log, and verify the ClassNotFoundError was logged.

Actual results:
The ClassNotFoundError is thrown by TestcaseContextListener.contextDestroyed,
and printed to localhost.log.  Any further work the context listener was
responsible for is not executed.

Expected Results:
The WebappClassLoader successfully loads classes during
ServletContextListener.contextDestroyed, and completes without a throw.  No
ClassNotFoundError is printed to localhost.log

Build Date & Hardware:
Tomcat 8.0.12 on CentOS Linux 6.6

Additional Builds and Platforms:
Tomcat 8.0.12 on Windows 7 SP1
Tomcat 8.0.20 on Windows 7 SP1
Tomcat 8.0.20 on CentOS Linux 6.6

Note: if you try the 'steps to reproduce' on 8.0.12, the deployment will
probably fail due to bug 56398 - which I worked around in 8.0.12.  You'll be
able to deploy if you change the name of the test app to
'classnotfoundgenerator'.

Additional Information:
ServletContextListeners that need to load classes during the contextDestroyed
call throw ClassNotFoundError, when the application is deployed as a WAR
through the Manager interface.  Here is an example listener, where
ThisClassNotFound is not loaded for the first time in contextDestroyed.
 
https://github.com/austinjones/ClassNotFoundGenerator/blob/master/src/com/avadyne/TestcaseContextListener.java

The stack trace of the ClassNotFoundError on 8.0.20 is:
6-Mar-2015 14:38:46.838 SEVERE [http-nio-8443-exec-5]
org.apache.catalina.core.StandardContext.listenerStop Exception sending context
destroyed event to listener instance of class
com.avadyne.TestcaseContextListener
 java.lang.NoClassDefFoundError: com/avadyne/ThisClassNotFound
    at
com.avadyne.TestcaseContextListener.contextDestroyed(TestcaseContextListener.java:27)
    at
org.apache.catalina.core.StandardContext.listenerStop(StandardContext.java:4775)
    at
org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5385)
    at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
    at
org.apache.catalina.core.StandardContext.reload(StandardContext.java:3739)
    at org.apache.catalina.startup.HostConfig.reload(HostConfig.java:1304)
    at
org.apache.catalina.startup.HostConfig.checkResources(HostConfig.java:1236)
    at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1491)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at
org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300)
    at
com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
    at
org.apache.catalina.manager.ManagerServlet.check(ManagerServlet.java:1460)
    at
org.apache.catalina.manager.ManagerServlet.deploy(ManagerServlet.java:906)
    at
org.apache.catalina.manager.ManagerServlet.doGet(ManagerServlet.java:344)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at
org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:108)
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:613)
    at
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
    at
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
    at
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
    at
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
    at
org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
    at
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
    at
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
    at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassNotFoundException: com.avadyne.ThisClassNotFound
    at
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1305)
    at
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1157)
    ... 45 more

The stack trace where Tomcat attempts to load ThisClassNotFound is (8.0.20):
Daemon Thread [http-nio-8443-exec-31] (Suspended)    
    owns: WebappClassLoader  (id=3483)    
    owns: TestcaseContextListener  (id=3488)    
    owns: StandardContext  (id=3324)    
    owns: HostConfig  (id=3323)    
    owns: SecureNioChannel  (id=3313)    
    StandardRoot.getResourceInternal(String, boolean) line: 302    
    Cache.getResource(String, boolean) line: 65    
    StandardRoot.getResource(String, boolean, boolean) line: 216    
    StandardRoot.getClassLoaderResource(String) line: 225    
    WebappClassLoader(WebappClassLoaderBase).findResourceInternal(String,
String) line: 2548    
    WebappClassLoader(WebappClassLoaderBase).findClassInternal(String) line:
2405    
    WebappClassLoader(WebappClassLoaderBase).findClass(String) line: 854    
    WebappClassLoader(WebappClassLoaderBase).loadClass(String, boolean) line:
1274    
    WebappClassLoader(WebappClassLoaderBase).loadClass(String) line: 1157    
    TestcaseContextListener.contextDestroyed(ServletContextEvent) line: 27    
    StandardContext.listenerStop() line: 4775    
    StandardContext.stopInternal() line: 5385    
    StandardContext(LifecycleBase).stop() line: 232    
    StandardContext.reload() line: 3739    
    HostConfig.reload(HostConfig$DeployedApplication) line: 1304    
    HostConfig.checkResources(HostConfig$DeployedApplication) line: 1236    
    HostConfig.check(String) line: 1491    
    NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not
available [native method]    
    NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62    
    DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43    
    Method.invoke(Object, Object...) line: 483    
    BaseModelMBean.invoke(String, Object[], String[]) line: 300    
    DefaultMBeanServerInterceptor.invoke(ObjectName, String, Object[],
String[]) line: 819    
    JmxMBeanServer.invoke(ObjectName, String, Object[], String[]) line: 801    
    ManagerServlet.check(String) line: 1460    
    ManagerServlet.deploy(PrintWriter, String, ContextName, String, boolean,
StringManager) line: 906    
    ManagerServlet.doGet(HttpServletRequest, HttpServletResponse) line: 344    
    ManagerServlet(HttpServlet).service(HttpServletRequest,
HttpServletResponse) line: 618    
    ManagerServlet(HttpServlet).service(ServletRequest, ServletResponse) line:
725    
    ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse)
line: 291    
    ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206  
    WsFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 52    
    ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse)
line: 239    
    ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206  
    SetCharacterEncodingFilter.doFilter(ServletRequest, ServletResponse,
FilterChain) line: 108    
    ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse)
line: 239    
    ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206  
    StandardWrapperValve.invoke(Request, Response) line: 219    
    StandardContextValve.invoke(Request, Response) line: 106    
    BasicAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 613   
    StandardHostValve.invoke(Request, Response) line: 142    
    ErrorReportValve.invoke(Request, Response) line: 79    
    AccessLogValve(AbstractAccessLogValve).invoke(Request, Response) line: 610  
    StandardEngineValve.invoke(Request, Response) line: 88    
    CoyoteAdapter.service(Request, Response) line: 516    
    Http11NioProcessor(AbstractHttp11Processor).process(SocketWrapper<S>) line:
1086    
   
Http11NioProtocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler).process(SocketWrapper<S>,
SocketStatus) line: 659    
   
Http11NioProtocol$Http11ConnectionHandler.process(SocketWrapper<NioChannel>,
SocketStatus) line: 223    
    NioEndpoint$SocketProcessor.doRun(SelectionKey, NioEndpoint$KeyAttachment)
line: 1558    
    NioEndpoint$SocketProcessor.run() line: 1515    
    ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker)
line: 1142    
    ThreadPoolExecutor$Worker.run() line: 617    
    TaskThread$WrappingRunnable.run() line: 61    
    TaskThread(Thread).run() line: 745    

The ClassLoader gives up on loading the class.  The WebResourceSet it would
have used to load the class from (in the method
StandardRoot.getResourceInternal) is a DirResourceSet pointed to the exploded
directory path - e.g. /path/to/tomcat/webapps/ClassNotFoundGenerator/.  The
path it generates in DirResourceSet.getResource is
/path/to/tomcat/webapps/ClassNotFoundGenerator/WEB-INF/classes/com/avadyne/ThisClassNotFound.class
- the path is correct.  The file doesn't exist, and it returns a new
EmptyResource(root, path, f).

When the WebappClassLoader attempts to load the class, the .class file (and the
entire exploded directory) is not on the disk.  The bug occurs during these
frames of the ClassNotFoundError stack trace (8.0.20).

 java.lang.NoClassDefFoundError: com/avadyne/ThisClassNotFound
...
     at
org.apache.catalina.core.StandardContext.reload(StandardContext.java:3739)
    at org.apache.catalina.startup.HostConfig.reload(HostConfig.java:1304)
    at
org.apache.catalina.startup.HostConfig.checkResources(HostConfig.java:1236)
...

HostConfig.checkResources:1231 deletes the exploded directory
HostConfig.checkResources:1236 calls into StandardContext.reload
StandardContext.reload:3739 eventually invokes the ServletContextListeners

Once the contextDestroyed listeners are executed, the .class file doesn't exist
(HostConfig.checkResources deleted it).  

It is unsafe for HostConfig.checkResources to delete the unpacked directory
before the context is stopped.  However, WebappClassLoader is correct to throw
a ClassNotFoundError, since the class doesn't exist on disk.

-- 
You are receiving this mail because:
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to