This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new fbd37c72c2 Fix BZ 70120. Don't re-use failed tags.
fbd37c72c2 is described below

commit fbd37c72c2b48174b0782c94b52d8f24067149b4
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Jun 23 17:29:42 2026 +0100

    Fix BZ 70120. Don't re-use failed tags.
    
    The original fix for BZ 69333 was over eager and caused a regression.
    The fix for the regression (BZ 69399) was incomplete. This commit
    completes the regression fix.
---
 java/org/apache/jasper/compiler/Generator.java     |  32 +++--
 .../apache/jasper/runtime/JspRuntimeLibrary.java   |   8 +-
 test/org/apache/jasper/compiler/TestGenerator.java | 138 +++++++++++++++++++++
 test/webapp/WEB-INF/bugs.tld                       |   5 +
 test/webapp/jsp/generator/reuse-exception.jsp      |  18 +++
 webapps/docs/changelog.xml                         |  11 ++
 6 files changed, 202 insertions(+), 10 deletions(-)

diff --git a/java/org/apache/jasper/compiler/Generator.java 
b/java/org/apache/jasper/compiler/Generator.java
index 239f13fcc2..914f386840 100644
--- a/java/org/apache/jasper/compiler/Generator.java
+++ b/java/org/apache/jasper/compiler/Generator.java
@@ -2322,6 +2322,10 @@ class Generator {
                 out.print(".get(");
                 out.print(tagHandlerClassName);
                 out.println(".class);");
+                out.printin("boolean ");
+                // Used to track if the tag is re-used successfully
+                out.print(tagHandlerVar);
+                out.println("_reused = false;");
             } else {
                 writeNewInstance(tagHandlerVar, tagHandlerClass);
             }
@@ -2513,22 +2517,32 @@ class Generator {
                 out.printil("}");
             }
 
+            // Print tag reuse
+            if (usePooling(n)) {
+                out.printin(n.getTagHandlerPoolName());
+                out.print(".reuse(");
+                out.print(tagHandlerVar);
+                out.println(");");
+                // This code will be skipped if an exception occurs which will 
prevent this instance from being re-used.
+                out.printin(tagHandlerVar);
+                out.println("_reused = true;");
+            }
+
             // Ensure clean-up takes place
+            // Use JspRuntimeLibrary to minimise code in _jspService()
             out.popIndent();
             out.printil("} finally {");
             out.pushIndent();
-
+            
out.printin("org.apache.jasper.runtime.JspRuntimeLibrary.releaseTag(");
+            out.print(tagHandlerVar);
+            out.print(", _jsp_getInstanceManager()");
             if (usePooling(n)) {
-                // Print tag reuse
-                out.printin(n.getTagHandlerPoolName());
-                out.print(".reuse(");
+                // The tag will only be released for reuse if no exceptions 
occurred during use.
+                out.print(", ");
                 out.print(tagHandlerVar);
-                out.println(");");
+                out.println("_reused);");
             } else {
-                // Clean-up
-                
out.printin("org.apache.jasper.runtime.JspRuntimeLibrary.releaseTag(");
-                out.print(tagHandlerVar);
-                out.println(", _jsp_getInstanceManager());");
+                out.print(");");
             }
             out.popIndent();
             out.printil("}");
diff --git a/java/org/apache/jasper/runtime/JspRuntimeLibrary.java 
b/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
index 0dcc368b06..4f7f10bfba 100644
--- a/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
+++ b/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
@@ -1222,7 +1222,13 @@ public class JspRuntimeLibrary {
         return out;
     }
 
-    @Deprecated
+    /**
+     * Releases a tag and destroys its instance it it has not been re-used.
+     *
+     * @param tag The tag to release
+     * @param instanceManager The instance manager
+     * @param reused Has the tag successfully been re-used
+     */
     public static void releaseTag(Tag tag, InstanceManager instanceManager, 
boolean reused) {
         // Caller ensures pool is non-null if reuse is true
         if (!reused) {
diff --git a/test/org/apache/jasper/compiler/TestGenerator.java 
b/test/org/apache/jasper/compiler/TestGenerator.java
index e53320c455..c56f78e9a9 100644
--- a/test/org/apache/jasper/compiler/TestGenerator.java
+++ b/test/org/apache/jasper/compiler/TestGenerator.java
@@ -23,13 +23,18 @@ import java.beans.PropertyEditorSupport;
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Field;
+import java.nio.channels.IllegalSelectorException;
 import java.nio.charset.CodingErrorAction;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Scanner;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.jsp.JspException;
 import javax.servlet.jsp.PageContext;
@@ -676,6 +681,81 @@ public class TestGenerator extends TomcatBaseTest {
         }
     }
 
+    public static class TesterExceptionTag extends TagSupport {
+
+        private static final long serialVersionUID = 1L;
+
+        private static AtomicInteger counter = new AtomicInteger(0);
+
+        private static Set<Integer> released = new HashSet<>();
+
+        private final int index;
+
+        private String state = "";
+
+        public static void resetForNewTest() {
+            counter.set(0);
+            released.clear();
+        }
+
+        public static boolean isReleased(int index) {
+            return released.contains(Integer.valueOf(index));
+        }
+
+        public TesterExceptionTag() {
+            index = counter.incrementAndGet();
+        }
+
+        @Override
+        public int doStartTag() throws JspException {
+            ServletRequest request = pageContext.getRequest();
+            boolean throwOnStart = 
Boolean.parseBoolean(request.getParameter("throwOnStart"));
+            if (throwOnStart) {
+                state += "lastStartFailed";
+                throw new IllegalSelectorException();
+            } else {
+                try {
+                    if (!state.isBlank()) {
+                        pageContext.getOut().print(state);
+                        pageContext.getOut().print("-");
+                    }
+                    pageContext.getOut().println("start-" + index);
+                } catch (IOException ioe) {
+                    throw new JspException(ioe);
+                }
+            }
+            return super.doStartTag();
+        }
+
+        @Override
+        public int doEndTag() throws JspException {
+            ServletRequest request = pageContext.getRequest();
+            boolean throwOnEnd = 
Boolean.parseBoolean(request.getParameter("throwOnEnd"));
+            if (throwOnEnd) {
+                state += "lastEndFailed";
+                throw new IllegalSelectorException();
+            } else {
+                try {
+                    if (!state.isBlank()) {
+                        pageContext.getOut().print(state);
+                        pageContext.getOut().print("-");
+                    }
+                    pageContext.getOut().println("end-" + index);
+                } catch (IOException ioe) {
+                    throw new JspException(ioe);
+                }
+            }
+            return super.doEndTag();
+        }
+
+        @Override
+        public void release() {
+            released.add(Integer.valueOf(index));
+            super.release();
+        }
+    }
+
+
     @Test
     public void testLambdaScriptlets() throws Exception {
         doTestJsp("lambda.jsp");
@@ -1176,4 +1256,62 @@ public class TestGenerator extends TomcatBaseTest {
                 + "application value=null", body.toString());
         body.recycle();
     }
+
+    @Test
+    public void testTagReuseException01() throws Exception {
+        TesterExceptionTag.resetForNewTest();
+        // No exceptions. Check re-use.
+        doTestTagReuseException(null, "start-1\nend-1\n", 
HttpServletResponse.SC_OK);
+        Assert.assertFalse(TesterExceptionTag.isReleased(1));
+        doTestTagReuseException(null, "start-1\nend-1\n", 
HttpServletResponse.SC_OK);
+        Assert.assertFalse(TesterExceptionTag.isReleased(1));
+    }
+
+    @Test
+    public void testTagReuseException02() throws Exception {
+        TesterExceptionTag.resetForNewTest();
+        // Exception on doStartTag
+        doTestTagReuseException(null, "start-1\nend-1\n", 
HttpServletResponse.SC_OK);
+        Assert.assertFalse(TesterExceptionTag.isReleased(1));
+        doTestTagReuseException("throwOnStart=true", null, 
HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        Assert.assertTrue(TesterExceptionTag.isReleased(1));
+        // Should use new instance
+        doTestTagReuseException(null, "start-2\nend-2\n", 
HttpServletResponse.SC_OK);
+        Assert.assertFalse(TesterExceptionTag.isReleased(2));
+        doTestTagReuseException(null, "start-2\nend-2\n", 
HttpServletResponse.SC_OK);
+        Assert.assertFalse(TesterExceptionTag.isReleased(2));
+    }
+
+    @Test
+    public void testTagReuseException03() throws Exception {
+        TesterExceptionTag.resetForNewTest();
+        // Exception on doEndTag
+        doTestTagReuseException(null, "start-1\nend-1\n", 
HttpServletResponse.SC_OK);
+        Assert.assertFalse(TesterExceptionTag.isReleased(1));
+        doTestTagReuseException("throwOnEnd=true", null, 
HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        Assert.assertTrue(TesterExceptionTag.isReleased(1));
+        // Should use new instance
+        doTestTagReuseException(null, "start-2\nend-2\n", 
HttpServletResponse.SC_OK);
+        Assert.assertFalse(TesterExceptionTag.isReleased(2));
+        doTestTagReuseException(null, "start-2\nend-2\n", 
HttpServletResponse.SC_OK);
+        Assert.assertFalse(TesterExceptionTag.isReleased(2));
+    }
+
+    private void doTestTagReuseException(String queryString, String 
expectedBody, int expectedStatus) throws Exception {
+        if (!getTomcatInstance().getServer().getState().isAvailable()) {
+            getTomcatInstanceTestWebapp(false, true);
+        }
+
+        ByteChunk body = new ByteChunk();
+        String uri = "/test/jsp/generator/reuse-exception.jsp";
+        if (queryString != null) {
+            uri += "?" + queryString;
+        }
+        int rc = getUrl("http://localhost:"; + getPort() + uri, body, null);
+
+        Assert.assertEquals(expectedStatus, rc);
+        if (expectedBody != null) {
+            Assert.assertEquals("Wrong body", expectedBody, body.toString());
+        }
+    }
 }
diff --git a/test/webapp/WEB-INF/bugs.tld b/test/webapp/WEB-INF/bugs.tld
index a4e496a833..5d9c89601b 100644
--- a/test/webapp/WEB-INF/bugs.tld
+++ b/test/webapp/WEB-INF/bugs.tld
@@ -183,6 +183,11 @@
       <type>jakarta.el.MethodExpression</type>
     </attribute>
   </tag>
+  <tag>
+    <name>TesterExceptionTag</name>
+    
<tag-class>org.apache.jasper.compiler.TestGenerator$TesterExceptionTag</tag-class>
+    <body-content>JSP</body-content>
+  </tag>
   <function>
     <name>bug49555</name>
     <function-class>org.apache.el.TesterFunctions$Inner$Class</function-class>
diff --git a/test/webapp/jsp/generator/reuse-exception.jsp 
b/test/webapp/jsp/generator/reuse-exception.jsp
new file mode 100644
index 0000000000..eaaf67ace6
--- /dev/null
+++ b/test/webapp/jsp/generator/reuse-exception.jsp
@@ -0,0 +1,18 @@
+<%--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--%><%@
+taglib uri="http://tomcat.apache.org/bugs"; prefix="bugs"
+%><bugs:TesterExceptionTag/>
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 38579578b1..83df27d443 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -136,6 +136,17 @@
       </fix>
     </changelog>
   </subsection>
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        <bug>70120</bug>: The fix for <bug>69699</bug> (itself a fix for a
+        regression in the fix for <bug>69333</bug>) was incomplete and tags 
that
+        threw exceptions in <code>doStartTag()</code> and
+        <code>doEndTag()</code> were incorrectly re-used. This fix prevents 
tags
+        from being re-used if such an exception occurs. (markt)
+      </fix>
+    </changelog>
+  </subsection>
 </section>
 <section name="Tomcat 9.0.119 (remm)" rtext="2026-06-23">
   <subsection name="Catalina">


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to