This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/11.0.x by this push:
new 7af55a6fea Fix BZ 70120. Don't re-use failed tags.
7af55a6fea is described below
commit 7af55a6fea8ec416b1cc5a38094efda24a8f70ff
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 | 15 +++
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, 210 insertions(+), 9 deletions(-)
diff --git a/java/org/apache/jasper/compiler/Generator.java
b/java/org/apache/jasper/compiler/Generator.java
index 26593e3dfa..446ac51f39 100644
--- a/java/org/apache/jasper/compiler/Generator.java
+++ b/java/org/apache/jasper/compiler/Generator.java
@@ -2088,6 +2088,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);
}
@@ -2279,22 +2283,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 170344312b..63ea44efe7 100644
--- a/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
+++ b/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
@@ -1219,6 +1219,21 @@ public class JspRuntimeLibrary {
}
+ /**
+ * 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) {
+ releaseTag(tag, instanceManager);
+ }
+ }
+
+
/**
* Releases a tag and destroys its instance.
*
diff --git a/test/org/apache/jasper/compiler/TestGenerator.java
b/test/org/apache/jasper/compiler/TestGenerator.java
index 41bbdca59f..de8e470fc1 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 jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.jsp.JspException;
import jakarta.servlet.jsp.PageContext;
@@ -738,6 +743,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");
@@ -1181,4 +1261,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 99b5f18f8c..a9be9e1f41 100644
--- a/test/webapp/WEB-INF/bugs.tld
+++ b/test/webapp/WEB-INF/bugs.tld
@@ -188,6 +188,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 846c47e11a..4506af8d7f 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 11.0.23 (markt)" rtext="2026-06-22">
<subsection name="Catalina">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]