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]