You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by bu...@apache.org on 2008/06/16 19:25:39 UTC

DO NOT REPLY [Bug 45214] New: Recompiled JSPs not reloaded as necessary

https://issues.apache.org/bugzilla/show_bug.cgi?id=45214

           Summary: Recompiled JSPs not reloaded as necessary
           Product: Tomcat 6
           Version: 6.0.16
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: major
          Priority: P2
         Component: Jasper
        AssignedTo: tomcat-dev@jakarta.apache.org
        ReportedBy: jhanlon@digitalriver.com


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 6.0.16.

---
apache-tomcat-6.0.16-src/apache-tomcat-6.0.16-src/java/org/apache/jasper/JspCompilationContext.java
2008-01-28 16:41:12.000000000 -0600
+++
apache-tomcat-6.0.16-src/apache-tomcat-6.0.16-src/java/org/apache/jasper/JspCompilationContext.java.fixed
  2008-06-16 12:18:27.227778400 -0500
@@ -183,10 +183,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,
@@ -560,11 +564,11 @@
     public void compile() throws JasperException, FileNotFoundException {
         createCompiler();
         if (isPackagedTagFile || jspCompiler.isOutDated()) {
             try {
                 jspCompiler.removeGeneratedFiles();
-                jspLoader = null;
+                this.resetJspLoader();
                 jspCompiler.compile();
                 jsw.setReload(true);
                 jsw.setCompilationException(null);
             } catch (JasperException ex) {
                 // Cache compilation exception


---
apache-tomcat-6.0.16-src/apache-tomcat-6.0.16-src/java/org/apache/jasper/servlet/JspServletWrapper.java
    2008-01-28 16:41:12.000000000 -0600
+++
apache-tomcat-6.0.16-src/apache-tomcat-6.0.16-src/java/org/apache/jasper/servlet/JspServletWrapper.java.fixed
      2008-06-16 12:20:11.027310600 -0500
@@ -123,10 +123,13 @@
         return ctxt;
     }

     public void setReload(boolean reload) {
         this.reload = reload;
+        if (reload) {
+            ctxt.resetJspLoader();
+        }
     }

     public Servlet getServlet()
         throws ServletException, IOException, FileNotFoundException
     {
@@ -192,11 +195,11 @@
     public void setServletClassLastModifiedTime(long lastModified) {
         if (this.servletClassLastModifiedTime < lastModified) {
             synchronized (this) {
                 if (this.servletClassLastModifiedTime < lastModified) {
                     this.servletClassLastModifiedTime = lastModified;
-                    reload = true;
+                    this.setReload(true);
                 }
             }
         }
     }



See also https://issues.apache.org/bugzilla/show_bug.cgi?id=45213 for report
against Tomcat 5.


-- 
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: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


DO NOT REPLY [Bug 45214] Recompiled JSPs not reloaded as necessary

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45214


Mark Thomas <ma...@apache.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |DUPLICATE




--- Comment #1 from Mark Thomas <ma...@apache.org>  2008-06-16 11:48:19 PST ---


*** This bug has been marked as a duplicate of bug 45213 ***


-- 
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: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org