https://issues.apache.org/bugzilla/show_bug.cgi?id=45213
Summary: Recompiled JSPs not reloaded as necessary Product: Tomcat 5 Version: 5.5.26 Platform: All OS/Version: All Status: NEW Severity: major Priority: P2 Component: Jasper AssignedTo: [EMAIL PROTECTED] ReportedBy: [EMAIL PROTECTED] CC: [EMAIL PROTECTED] In a Tomcat server farm utilizing a shared work directory (that is, all Tomcat instances have the virtual 'Host' element attribute 'workDir' pointing to the same directory) it is possibly (even likely) that one Tomcat instance may recompile a changed JSP that another instance has already loaded. Under these circumstances, the second Tomcat instance will fail to reload the newly recompiled JSP. Method to Duplicate: 1) Bring up two instances of Tomcat (call them node1 and node2) that share a set of JSPs and a work directory (each node's server.xml has the default virtual host's appBase and workDir pointing to common shared directories). 2) For each hit a common JSP 3) Suspend the process running Tomcat for node1 ... a) On windows you can use the Process Explorer (http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx) to suspend the process (right click on process and select Suspend). b) On *nix you can use send it a SIGSTOP to suspend it. 4) Modify the JSP. 5) Hit the page on node2. You should see the modification. 6) Resume the suspended appserver process. (on unix you send SIGCONT; Process Explorer right click on process and select Resume). 7) Hit the page on node1. Without this fix you should *not* see the modification. With the fix you should see the changes. Analysis: The shared JSP is correctly flagged for recompile and rebuilt. Note that there is a race condition (not the subject of the bug report) wherein any number (from one to all) Tomcat instances may try to recompile the changed JSP at the same time, depending on the timing of their JspRuntimeContext (JSP recompilation) threads. In the the procedure above we suspend one server to control which server performs the recompilation. The awakened server will (within its JspRuntimeContext thread) detect that the JSP has changed and will "request" that it be reloaded. This occurs in JspServletWrapper.setServletClassLastModifiedTime in the call to JspServletWrapper.setReload(boolean) ... the calling sequence will be approximately JspRuntimeContext.checkCompile() -> JspCompilationContext.compile() -> Compiler.isOutDated() -> Compiler(isOutDated(boolean=true) -> JspServletWrapper.setServletClassLastModifiedTime(long) -> JspServletWrapper.setReload(boolean=true). JspServletWrapper.getServlet() will try to reload the JSP by calling JspCompilationContext.load(). However, the call to load() will return the *old* class, not the *new* one. The underlying reason is that JspCompilationContext's class loader (property jspLoader) was never reset as a side effect of JspServletWrapper.setReload(boolean=true). As a result, the subsequent call to JspCompilationContext.load() continues to use the old class loader, and hence return the old class. At present, the JspCompilationContext class loader is only reset as a side effect of performing a compilation *by this instance of Tomcat*. Thus an Tomcat instance may notice that a JSP has been recompiled (by another Tomcat instance) but will never reload the class because *it* didn't perform the recompiation. Fix: The following modification to Jasper addresses this behavior: - add a resetJspLoader() method to JspCompilationContext which throws away the current class loader thus allowing the JSP to be reloaded. - modify the setReload(boolean) method on JspServletWrapper to invoke JspCompilationContext.resetJspLoader() if the reload argument is true. Diffs: The following diffs are against 5.5.20. The same modifications apply to 5.5.26 and 6.0.16, only the line numbers differ. --- /apache-tomcat-5.5.20-src/jasper/src/share/org/apache/jasper/JspCompilationContext.java +++ /fix/org/apache/jasper/JspCompilationContext.java @@ -182,10 +182,14 @@ rctxt.getCodeSource()); } return jspLoader; } + public void resetJspLoader() { + this.jspLoader = null; + } + /** ---------- Input/Output ---------- */ /** * The output directory to generate code into. The output directory * is make up of the scratch directory, which is provide in Options, @@ -557,11 +561,11 @@ public void compile() throws JasperException, FileNotFoundException { createCompiler(); if (isPackagedTagFile || jspCompiler.isOutDated()) { try { - jspLoader = null; + this.resetJspLoader(); jspCompiler.compile(); jsw.setReload(true); jsw.setCompilationException(null); } catch (JasperException ex) { // Cache compilation exception --- apache-tomcat-5.5.20/jasper/src/share/org/apache/jasper/servlet/JspServletWrapper.java +++ /fix/org/apache/jasper/servlet/JspServletWrapper.java @@ -128,10 +128,13 @@ return ctxt; } public void setReload(boolean reload) { this.reload = reload; + if (reload) { + ctxt.resetJspLoader(); + } } public Servlet getServlet() throws ServletException, IOException, FileNotFoundException { @@ -187,11 +190,11 @@ public void setServletClassLastModifiedTime(long lastModified) { if (this.servletClassLastModifiedTime < lastModified) { synchronized (this) { if (this.servletClassLastModifiedTime < lastModified) { this.servletClassLastModifiedTime = lastModified; - reload = true; + this.setReload(true); } } } } -- Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug. --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]