Bug 57743

Summary: Jar files in <webapp>/WEB-INF/lib are kept open, preventing redeploy
Product: Tomcat 8 Reporter: Pavel Avgustinov <pavel>
Component: CatalinaAssignee: Tomcat Developers Mailing List <dev>
Severity: normal    
Priority: P2    
Version: 8.0.20   
Target Milestone: ----   
Hardware: PC   
OS: Linux   
Attachments: A patch against AbstractResourceSet.java, adding a call to gc() when it is destroyed.

Description Pavel Avgustinov 2015-03-23 12:57:51 UTC
Created attachment 32592 [details]
A patch against AbstractResourceSet.java, adding a call to gc() when it is destroyed.

As described in 56390, keeping jar files in a webapp folder open will prevent the application from being undeployed/redeployed on file systems which don't like open files to be deleted (in practice, that seems to mean Windows-based systems or NFS mounts).

Having observed the problem with tomcat 8.0.20, which is meant to contain the fix for 56390, I believe I have uncovered the root cause. Here's what happens, in my scenario:

 - A new .war is copied into $CATALINA_HOME/webapps; this is picked up by `HostConfig.checkResources()`, which decides to redeploy the app.

 - To do this, it first undeploys it (HostConfig.java:1250 in the TOMCAT_8_0_20 tag), and then attempts to delete the exploded war (line 1251).

 - In my case, the undeploy triggered a servlet's `destroy()` method, which triggered some class loading from WEB-INF/lib. Thus, the context's `WebResourceRoot` was used immediately before the attempt to delete the webapp folder, and opened a jar file.

 - Even though the `JarInputStreamWrapper` was properly closed, the handle to the jar remains open -- by design of `AbstractArchiveResource`, it is not closed immediately but with the next periodically scheduled call to `gc()`, as part of `StandardRoot.backgroundProcess()`.

As a result, when $CATALINA_HOME is on an NFS mount tomcat fails to delete the exploded webapp directory, and things go bad.

The attached patch fixes the problem in my testing; it simply changes `AbstractResourceSet.destroyInternal()` so that instead of doing nothing it calls `gc()`, which will close any jar files which no longer have open input streams. This seems like it's necessary to avoid a resource leak even on systems that allow the deletion of open files, since if I'm not mistaken after the context has been detached it will no longer receive `gc()` calls from the background process thread.
Comment 1 Mark Thomas 2015-03-23 13:56:24 UTC
Thanks for the very clear bug report and analysis.

Both the analysis and patch look good so the patch has been applied to trunk and 8.0.x for 8.0.21 onwards.

Thanks again for your contribution.