You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2021/09/23 21:18:36 UTC

[tomcat] 03/03: Implement the new page/tag directive errorOnELNotFound

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

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

commit 37bc60c9efed390f662afe0a1d12d2ba0d91ada8
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Thu Sep 23 21:09:52 2021 +0100

    Implement the new page/tag directive errorOnELNotFound
---
 java/jakarta/servlet/jsp/resources/jspxml.dtd      |  27 ++--
 java/jakarta/servlet/jsp/resources/jspxml.xsd      |   1 +
 java/org/apache/jasper/compiler/Compiler.java      |   4 +
 java/org/apache/jasper/compiler/Generator.java     |  15 +++
 java/org/apache/jasper/compiler/JspConfig.java     |  21 ++-
 java/org/apache/jasper/compiler/PageInfo.java      |  40 ++++++
 .../apache/jasper/compiler/TagFileProcessor.java   |   3 +-
 java/org/apache/jasper/compiler/Validator.java     |  15 +++
 .../jasper/resources/LocalStrings.properties       |   4 +
 .../apache/jasper/runtime/JspContextWrapper.java   |   9 ++
 .../apache/jasper/runtime/JspSourceDirectives.java |  27 ++++
 .../org/apache/jasper/runtime/PageContextImpl.java |   6 +
 .../util/descriptor/web/JspPropertyGroup.java      |   6 +
 .../web/JspPropertyGroupDescriptorImpl.java        |  12 ++
 .../tomcat/util/descriptor/web/WebRuleSet.java     |   2 +
 test/org/apache/jasper/compiler/TestJspConfig.java | 145 +++++++++++++++++++++
 .../jasper/servlet/TestJspCServletContext.java     |   2 +-
 .../WEB-INF/tags/error-on-el-not-found-false.tag   |  18 +++
 .../WEB-INF/tags/error-on-el-not-found-true.tag    |  18 +++
 test/webapp/WEB-INF/web.xml                        |   8 ++
 test/webapp/jsp/errorOnELNotFound/default.jsp      |  21 +++
 .../jsp/errorOnELNotFound/page-directive-false.jsp |  22 ++++
 .../jsp/errorOnELNotFound/page-directive-true.jsp  |  22 ++++
 .../jsp/errorOnELNotFound/tag-file-false.jsp       |  23 ++++
 .../webapp/jsp/errorOnELNotFound/tag-file-true.jsp |  23 ++++
 .../webapp/jsp/errorOnELNotFound/web-xml-false.jsp |  21 +++
 test/webapp/jsp/errorOnELNotFound/web-xml-true.jsp |  21 +++
 webapps/docs/changelog.xml                         |   5 +
 28 files changed, 524 insertions(+), 17 deletions(-)

diff --git a/java/jakarta/servlet/jsp/resources/jspxml.dtd b/java/jakarta/servlet/jsp/resources/jspxml.dtd
index c6f0998..a5fcc5f 100644
--- a/java/jakarta/servlet/jsp/resources/jspxml.dtd
+++ b/java/jakarta/servlet/jsp/resources/jspxml.dtd
@@ -94,19 +94,20 @@
 
 <!ELEMENT jsp:directive.page EMPTY>
 <!ATTLIST jsp:directive.page
-    language        CDATA           "java"
-    extends         %ClassName;     #IMPLIED
-    contentType     %Content;       "text/html; charset=ISO-8859-1"
-    import          CDATA           #IMPLIED
-    session         %Bool;          "true"
-    buffer          CDATA           "8kb"
-    autoFlush       %Bool;          "true"
-    isThreadSafe    %Bool;          "true"
-    info            CDATA           #IMPLIED
-    errorPage       %URL;           #IMPLIED
-    isErrorPage     %Bool;          "false"
-    pageEncoding    CDATA           #IMPLIED
-    isELIgnored     %Bool;          #IMPLIED
+    language            CDATA           "java"
+    extends             %ClassName;     #IMPLIED
+    contentType         %Content;       "text/html; charset=ISO-8859-1"
+    import              CDATA           #IMPLIED
+    session             %Bool;          "true"
+    buffer              CDATA           "8kb"
+    autoFlush           %Bool;          "true"
+    isThreadSafe        %Bool;          "true"
+    info                CDATA           #IMPLIED
+    errorPage           %URL;           #IMPLIED
+    isErrorPage         %Bool;          "false"
+    pageEncoding        CDATA           #IMPLIED
+    isELIgnored         %Bool;          #IMPLIED
+    errorOnELNotFound   %Bool;          "false"
 >
 
 <!-- the jsp:directive.include only appears in JSP documents and does
diff --git a/java/jakarta/servlet/jsp/resources/jspxml.xsd b/java/jakarta/servlet/jsp/resources/jspxml.xsd
index 0e5eba0..4cad6bb 100644
--- a/java/jakarta/servlet/jsp/resources/jspxml.xsd
+++ b/java/jakarta/servlet/jsp/resources/jspxml.xsd
@@ -333,6 +333,7 @@
       <xsd:attribute name = "errorPage" type = "RelativeURL"/>
       <xsd:attribute name = "isErrorPage" default = "false" type = "Bool"/>
       <xsd:attribute name = "isELIgnored" type = "Bool"/>
+      <xsd:attribute name = "errorOnELNotFound" default = "false" type = "Bool"/>
     </xsd:complexType>
   </xsd:element>
 
diff --git a/java/org/apache/jasper/compiler/Compiler.java b/java/org/apache/jasper/compiler/Compiler.java
index 7da6055..b38edae 100644
--- a/java/org/apache/jasper/compiler/Compiler.java
+++ b/java/org/apache/jasper/compiler/Compiler.java
@@ -132,6 +132,10 @@ public abstract class Compiler {
             pageInfo.setELIgnored(JspUtil.booleanValue(jspProperty
                     .isELIgnored()));
         }
+        if (jspProperty.getErrorOnELNotFound() != null) {
+            pageInfo.setErrorOnELNotFound(JspUtil.booleanValue(jspProperty
+                    .getErrorOnELNotFound()));
+        }
         if (jspProperty.isScriptingInvalid() != null) {
             pageInfo.setScriptingInvalid(JspUtil.booleanValue(jspProperty
                     .isScriptingInvalid()));
diff --git a/java/org/apache/jasper/compiler/Generator.java b/java/org/apache/jasper/compiler/Generator.java
index 61eec0c..2872e01 100644
--- a/java/org/apache/jasper/compiler/Generator.java
+++ b/java/org/apache/jasper/compiler/Generator.java
@@ -700,6 +700,17 @@ class Generator {
         out.printil("}");
         out.println();
 
+        // Implement JspSourceDirectives
+        out.printil("public boolean getErrorOnELNotFound() {");
+        out.pushIndent();
+        if (pageInfo.isErrorOnELNotFound()) {
+            out.printil("return true;");
+        } else {
+            out.printil("return false;");
+        }
+        out.popIndent();
+        out.printil("}");
+        out.println();
 
         generateGetters();
         generateInit();
@@ -733,6 +744,8 @@ class Generator {
             out.println(",");
             out.printin("                 jakarta.servlet.SingleThreadModel");
         }
+        out.println(",");
+        out.printin("                 org.apache.jasper.runtime.JspSourceDirectives");
         out.println(" {");
         out.pushIndent();
 
@@ -3652,6 +3665,8 @@ class Generator {
             out.println(",");
             out.printin("               jakarta.servlet.jsp.tagext.DynamicAttributes");
         }
+        out.println(",");
+        out.printin("                 org.apache.jasper.runtime.JspSourceDirectives");
         out.println(" {");
         out.pushIndent();
 
diff --git a/java/org/apache/jasper/compiler/JspConfig.java b/java/org/apache/jasper/compiler/JspConfig.java
index f63109a..7cb3b64 100644
--- a/java/org/apache/jasper/compiler/JspConfig.java
+++ b/java/org/apache/jasper/compiler/JspConfig.java
@@ -46,6 +46,7 @@ public class JspConfig {
 
     private static final String defaultIsXml = null;    // unspecified
     private String defaultIsELIgnored = null;           // unspecified
+    private String defaultErrorOnELNotFound = "false";
     private static final String defaultIsScriptingInvalid = null;
     private String defaultDeferedSyntaxAllowedAsLiteral = null;
     private static final String defaultTrimDirectiveWhitespaces = null;
@@ -96,6 +97,7 @@ public class JspConfig {
 
             JspProperty property = new JspProperty(jspPropertyGroup.getIsXml(),
                     jspPropertyGroup.getElIgnored(),
+                    jspPropertyGroup.getErrorOnELNotFound(),
                     jspPropertyGroup.getScriptingInvalid(),
                     jspPropertyGroup.getPageEncoding(),
                     jspPropertyGroup.getIncludePreludes(),
@@ -164,6 +166,7 @@ public class JspConfig {
                     processWebDotXml();
                     defaultJspProperty = new JspProperty(defaultIsXml,
                             defaultIsELIgnored,
+                            defaultErrorOnELNotFound,
                             defaultIsScriptingInvalid,
                             null, null, null,
                             defaultDeferedSyntaxAllowedAsLiteral,
@@ -245,6 +248,7 @@ public class JspConfig {
 
         JspPropertyGroup isXmlMatch = null;
         JspPropertyGroup elIgnoredMatch = null;
+        JspPropertyGroup errorOnELNotFoundMatch = null;
         JspPropertyGroup scriptingInvalidMatch = null;
         JspPropertyGroup pageEncodingMatch = null;
         JspPropertyGroup deferedSyntaxAllowedAsLiteralMatch = null;
@@ -296,6 +300,9 @@ public class JspConfig {
             if (jp.isELIgnored() != null) {
                 elIgnoredMatch = selectProperty(elIgnoredMatch, jpg);
             }
+            if (jp.getErrorOnELNotFound() != null) {
+                errorOnELNotFoundMatch = selectProperty(errorOnELNotFoundMatch, jpg);
+            }
             if (jp.isScriptingInvalid() != null) {
                 scriptingInvalidMatch =
                     selectProperty(scriptingInvalidMatch, jpg);
@@ -327,6 +334,7 @@ public class JspConfig {
 
         String isXml = defaultIsXml;
         String isELIgnored = defaultIsELIgnored;
+        String errorOnELNotFound = defaultErrorOnELNotFound;
         String isScriptingInvalid = defaultIsScriptingInvalid;
         String pageEncoding = null;
         String isDeferedSyntaxAllowedAsLiteral = defaultDeferedSyntaxAllowedAsLiteral;
@@ -338,6 +346,9 @@ public class JspConfig {
         if (isXmlMatch != null) {
             isXml = isXmlMatch.getJspProperty().isXml();
         }
+        if (errorOnELNotFoundMatch != null) {
+            errorOnELNotFound = errorOnELNotFoundMatch.getJspProperty().getErrorOnELNotFound();
+        }
         if (elIgnoredMatch != null) {
             isELIgnored = elIgnoredMatch.getJspProperty().isELIgnored();
         }
@@ -368,7 +379,7 @@ public class JspConfig {
                 errorOnUndeclaredNamespaceMatch.getJspProperty().isErrorOnUndeclaredNamespace();
         }
 
-        return new JspProperty(isXml, isELIgnored, isScriptingInvalid,
+        return new JspProperty(isXml, isELIgnored, errorOnELNotFound, isScriptingInvalid,
                 pageEncoding, includePreludes, includeCodas,
                 isDeferedSyntaxAllowedAsLiteral, isTrimDirectiveWhitespaces,
                 defaultContentType, buffer, errorOnUndeclaredNamespace);
@@ -448,6 +459,7 @@ public class JspConfig {
 
         private final String isXml;
         private final String elIgnored;
+        private final String errorOnELNotFound;
         private final String scriptingInvalid;
         private final String pageEncoding;
         private final Collection<String> includePrelude;
@@ -458,7 +470,7 @@ public class JspConfig {
         private final String buffer;
         private final String errorOnUndeclaredNamespace;
 
-        public JspProperty(String isXml, String elIgnored,
+        public JspProperty(String isXml, String elIgnored, String errorOnELNotFound,
                 String scriptingInvalid, String pageEncoding,
                 Collection<String> includePrelude, Collection<String> includeCoda,
                 String deferedSyntaxAllowedAsLiteral,
@@ -469,6 +481,7 @@ public class JspConfig {
 
             this.isXml = isXml;
             this.elIgnored = elIgnored;
+            this.errorOnELNotFound = errorOnELNotFound;
             this.scriptingInvalid = scriptingInvalid;
             this.pageEncoding = pageEncoding;
             this.includePrelude = includePrelude;
@@ -488,6 +501,10 @@ public class JspConfig {
             return elIgnored;
         }
 
+        public String getErrorOnELNotFound() {
+            return errorOnELNotFound;
+        }
+
         public String isScriptingInvalid() {
             return scriptingInvalid;
         }
diff --git a/java/org/apache/jasper/compiler/PageInfo.java b/java/org/apache/jasper/compiler/PageInfo.java
index 464a5c9..5c0a2c0 100644
--- a/java/org/apache/jasper/compiler/PageInfo.java
+++ b/java/org/apache/jasper/compiler/PageInfo.java
@@ -100,6 +100,10 @@ class PageInfo {
     // JSP 2.2
     private boolean errorOnUndeclaredNamespace = false;
 
+    // JSP 3.1
+    private String errorOnELNotFoundValue;
+    private boolean errorOnELNotFound = false;
+
     private final boolean isTagFile;
 
     PageInfo(BeanRepository beanRepository, JspCompilationContext ctxt) {
@@ -635,6 +639,30 @@ class PageInfo {
         isELIgnoredValue = value;
     }
 
+
+    /*
+     * errorOnELNotFound
+     */
+    public void setErrorOnELNotFound(String value, Node n, ErrorDispatcher err,
+                   boolean pagedir)
+        throws JasperException {
+
+        if ("true".equalsIgnoreCase(value)) {
+            errorOnELNotFound = true;
+        } else if ("false".equalsIgnoreCase(value)) {
+            errorOnELNotFound = false;
+        } else {
+            if (pagedir) {
+                err.jspError(n, "jsp.error.page.invalid.errorOnELNotFound");
+            } else {
+                err.jspError(n, "jsp.error.tag.invalid.errorOnELNotFound");
+            }
+        }
+
+        errorOnELNotFoundValue = value;
+    }
+
+
     /*
      * deferredSyntaxAllowedAsLiteral
      */
@@ -691,6 +719,18 @@ class PageInfo {
         return isELIgnored;
     }
 
+    public void setErrorOnELNotFound(boolean s) {
+        errorOnELNotFound = s;
+    }
+
+    public String getErrorOnELNotFound() {
+        return errorOnELNotFoundValue;
+    }
+
+    public boolean isErrorOnELNotFound() {
+        return errorOnELNotFound;
+    }
+
     public void putNonCustomTagPrefix(String prefix, Mark where) {
         nonCustomTagPrefixMap.put(prefix, where);
     }
diff --git a/java/org/apache/jasper/compiler/TagFileProcessor.java b/java/org/apache/jasper/compiler/TagFileProcessor.java
index e6a1773..e1ed7c1 100644
--- a/java/org/apache/jasper/compiler/TagFileProcessor.java
+++ b/java/org/apache/jasper/compiler/TagFileProcessor.java
@@ -68,7 +68,8 @@ class TagFileProcessor {
                 new JspUtil.ValidAttribute("import"),
                 new JspUtil.ValidAttribute("deferredSyntaxAllowedAsLiteral"), // JSP 2.1
                 new JspUtil.ValidAttribute("trimDirectiveWhitespaces"), // JSP 2.1
-                new JspUtil.ValidAttribute("isELIgnored") };
+                new JspUtil.ValidAttribute("isELIgnored"),
+                new JspUtil.ValidAttribute("errorOnELNotFound") };
 
         private static final JspUtil.ValidAttribute[] attributeDirectiveAttrs = {
                 new JspUtil.ValidAttribute("name", true),
diff --git a/java/org/apache/jasper/compiler/Validator.java b/java/org/apache/jasper/compiler/Validator.java
index 61bc587..94d0b2c 100644
--- a/java/org/apache/jasper/compiler/Validator.java
+++ b/java/org/apache/jasper/compiler/Validator.java
@@ -82,6 +82,7 @@ class Validator {
             new JspUtil.ValidAttribute("contentType"),
             new JspUtil.ValidAttribute("pageEncoding"),
             new JspUtil.ValidAttribute("isELIgnored"),
+            new JspUtil.ValidAttribute("errorOnELNotFound"),
             new JspUtil.ValidAttribute("deferredSyntaxAllowedAsLiteral"),
             new JspUtil.ValidAttribute("trimDirectiveWhitespaces")
         };
@@ -174,6 +175,13 @@ class Validator {
                         err.jspError(n, "jsp.error.page.conflict.iselignored",
                                 pageInfo.getIsELIgnored(), value);
                     }
+                } else if ("errorOnELNotFound".equals(attr)) {
+                    if (pageInfo.getErrorOnELNotFound() == null) {
+                        pageInfo.setErrorOnELNotFound(value, n, err, true);
+                    } else if (!pageInfo.getErrorOnELNotFound().equals(value)) {
+                        err.jspError(n, "jsp.error.page.conflict.errorOnELNotFound",
+                                pageInfo.getErrorOnELNotFound(), value);
+                    }
                 } else if ("isErrorPage".equals(attr)) {
                     if (pageInfo.getIsErrorPage() == null) {
                         pageInfo.setIsErrorPage(value, n, err);
@@ -270,6 +278,13 @@ class Validator {
                         err.jspError(n, "jsp.error.tag.conflict.iselignored",
                                 pageInfo.getIsELIgnored(), value);
                     }
+                } else if ("errorOnELNotFound".equals(attr)) {
+                    if (pageInfo.getErrorOnELNotFound() == null) {
+                        pageInfo.setErrorOnELNotFound(value, n, err, false);
+                    } else if (!pageInfo.getErrorOnELNotFound().equals(value)) {
+                        err.jspError(n, "jsp.error.tag.conflict.errorOnELNotFound",
+                                pageInfo.getErrorOnELNotFound(), value);
+                    }
                 } else if ("pageEncoding".equals(attr)) {
                     if (pageEncodingSeen) {
                         err.jspError(n, "jsp.error.tag.multi.pageencoding");
diff --git a/java/org/apache/jasper/resources/LocalStrings.properties b/java/org/apache/jasper/resources/LocalStrings.properties
index 898e173..3a0f990 100644
--- a/java/org/apache/jasper/resources/LocalStrings.properties
+++ b/java/org/apache/jasper/resources/LocalStrings.properties
@@ -143,6 +143,7 @@ jsp.error.page.conflict.autoflush=Page directive: illegal to have multiple occur
 jsp.error.page.conflict.buffer=Page directive: illegal to have multiple occurrences of ''buffer'' with different values (old: [{0}], new: [{1}])
 jsp.error.page.conflict.contenttype=Page directive: illegal to have multiple occurrences of ''contentType'' with different values (old: [{0}], new: [{1}])
 jsp.error.page.conflict.deferredsyntaxallowedasliteral=Page directive: illegal to have multiple occurrences of ''deferredSyntaxAllowedAsLiteral'' with different values (old: [{0}], new: [{1}])
+jsp.error.page.conflict.errorOnELNotFound=Page directive: illegal to have multiple occurrences of ''errorOnELNotFound'' with different values (old: [{0}], new: [{1}])
 jsp.error.page.conflict.errorpage=Page directive: illegal to have multiple occurrences of ''errorPage'' with different values (old: [{0}], new: [{1}])
 jsp.error.page.conflict.extends=Page directive: illegal to have multiple occurrences of ''extends'' with different values (old: [{0}], new: [{1}])
 jsp.error.page.conflict.info=Page directive: illegal to have multiple occurrences of ''info'' with different values (old: [{0}], new: [{1}])
@@ -154,6 +155,7 @@ jsp.error.page.conflict.session=Page directive: illegal to have multiple occurre
 jsp.error.page.conflict.trimdirectivewhitespaces=Page directive: illegal to have multiple occurrences of ''trimDirectiveWhitespaces'' with different values (old: [{0}], new: [{1}])
 jsp.error.page.invalid.buffer=Page directive: invalid value for buffer
 jsp.error.page.invalid.deferredsyntaxallowedasliteral=Page directive: invalid value for deferredSyntaxAllowedAsLiteral
+jsp.error.page.invalid.errorOnELNotFound=Page directive: invalid value for errorOnELNotFound
 jsp.error.page.invalid.import=Page directive: invalid value for import
 jsp.error.page.invalid.iselignored=Page directive: invalid value for isELIgnored
 jsp.error.page.invalid.iserrorpage=Page directive: invalid value for isErrorPage
@@ -200,10 +202,12 @@ jsp.error.stream.closed=Stream closed
 jsp.error.string_interpreter_class.instantiation=Failed to load or instantiate StringInterpreter class [{0}]
 jsp.error.tag.conflict.attr=Tag directive: illegal to have multiple occurrences of the attribute [{0}] with different values (old: [{1}], new: [{2}])
 jsp.error.tag.conflict.deferredsyntaxallowedasliteral=Tag directive: illegal to have multiple occurrences of ''deferredSyntaxAllowedAsLiteral'' with different values (old: [{0}], new: [{1}])
+jsp.error.tag.conflict.errorOnELNotFound=Tag directive: illegal to have multiple occurrences of ''errorOnELNotFound'' with different values (old: [{0}], new: [{1}])
 jsp.error.tag.conflict.iselignored=Tag directive: illegal to have multiple occurrences of ''isELIgnored'' with different values (old: [{0}], new: [{1}])
 jsp.error.tag.conflict.language=Tag directive: illegal to have multiple occurrences of ''language'' with different values (old: [{0}], new: [{1}])
 jsp.error.tag.conflict.trimdirectivewhitespaces=Tag directive: illegal to have multiple occurrences of ''trimDirectiveWhitespaces'' with different values (old: [{0}], new: [{1}])
 jsp.error.tag.invalid.deferredsyntaxallowedasliteral=Tag directive: invalid value for deferredSyntaxAllowedAsLiteral
+jsp.error.tag.invalid.errorOnELNotFound=Tag directive: invalid value for errorOnELNotFound
 jsp.error.tag.invalid.iselignored=Tag directive: invalid value for isELIgnored
 jsp.error.tag.invalid.trimdirectivewhitespaces=Tag directive: invalid value for trimDirectiveWhitespaces
 jsp.error.tag.language.nonjava=Tag directive: invalid language attribute
diff --git a/java/org/apache/jasper/runtime/JspContextWrapper.java b/java/org/apache/jasper/runtime/JspContextWrapper.java
index 718c803..a7820a7 100644
--- a/java/org/apache/jasper/runtime/JspContextWrapper.java
+++ b/java/org/apache/jasper/runtime/JspContextWrapper.java
@@ -48,6 +48,7 @@ import jakarta.servlet.jsp.JspWriter;
 import jakarta.servlet.jsp.PageContext;
 import jakarta.servlet.jsp.el.ELException;
 import jakarta.servlet.jsp.el.ExpressionEvaluator;
+import jakarta.servlet.jsp.el.NotFoundELResolver;
 import jakarta.servlet.jsp.el.VariableResolver;
 import jakarta.servlet.jsp.tagext.BodyContent;
 import jakarta.servlet.jsp.tagext.JspTag;
@@ -563,6 +564,14 @@ public class JspContextWrapper extends PageContext implements VariableResolver {
             if (key == JspContext.class) {
                 return pageContext;
             }
+            if (key == NotFoundELResolver.class) {
+                if (jspTag instanceof JspSourceDirectives) {
+                    return Boolean.valueOf(((JspSourceDirectives) jspTag).getErrorOnELNotFound());
+                } else {
+                    // returning Boolean.FALSE would have the same effect
+                    return null;
+                }
+            }
             return wrapped.getContext(key);
         }
 
diff --git a/java/org/apache/jasper/runtime/JspSourceDirectives.java b/java/org/apache/jasper/runtime/JspSourceDirectives.java
new file mode 100644
index 0000000..cb531ac
--- /dev/null
+++ b/java/org/apache/jasper/runtime/JspSourceDirectives.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package org.apache.jasper.runtime;
+
+/**
+ * Provides runtime access to selected compile time directives. Page directives
+ * are not added to this interface until there is a requirement to access them
+ *  at runtime.
+ */
+public interface JspSourceDirectives {
+
+    boolean getErrorOnELNotFound();
+}
diff --git a/java/org/apache/jasper/runtime/PageContextImpl.java b/java/org/apache/jasper/runtime/PageContextImpl.java
index 1477013..3000439 100644
--- a/java/org/apache/jasper/runtime/PageContextImpl.java
+++ b/java/org/apache/jasper/runtime/PageContextImpl.java
@@ -43,6 +43,7 @@ import jakarta.servlet.jsp.JspException;
 import jakarta.servlet.jsp.JspFactory;
 import jakarta.servlet.jsp.JspWriter;
 import jakarta.servlet.jsp.PageContext;
+import jakarta.servlet.jsp.el.NotFoundELResolver;
 import jakarta.servlet.jsp.tagext.BodyContent;
 
 import org.apache.jasper.Constants;
@@ -721,6 +722,11 @@ public class PageContextImpl extends PageContext {
                     }
                 }
             }
+            if (servlet instanceof JspSourceDirectives) {
+                if (((JspSourceDirectives) servlet).getErrorOnELNotFound()) {
+                    elContext.putContext(NotFoundELResolver.class, Boolean.TRUE);
+                }
+            }
         }
         return this.elContext;
     }
diff --git a/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroup.java b/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroup.java
index 9246c60..07f3dd3 100644
--- a/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroup.java
+++ b/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroup.java
@@ -34,6 +34,12 @@ public class JspPropertyGroup extends XmlEncodingBase {
     }
     public Boolean getDeferredSyntax() { return deferredSyntax; }
 
+    private Boolean errorOnELNotFound = null;
+    public void setErrorOnELNotFound(String errorOnELNotFound) {
+        this.errorOnELNotFound = Boolean.valueOf(errorOnELNotFound);
+    }
+    public Boolean getErrorOnELNotFound() { return errorOnELNotFound; }
+
     private Boolean elIgnored = null;
     public void setElIgnored(String elIgnored) {
         this.elIgnored = Boolean.valueOf(elIgnored);
diff --git a/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroupDescriptorImpl.java b/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroupDescriptorImpl.java
index 4785ee2..0f67f61 100644
--- a/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroupDescriptorImpl.java
+++ b/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroupDescriptorImpl.java
@@ -72,6 +72,18 @@ public class JspPropertyGroupDescriptorImpl
 
 
     @Override
+    public String getErrorOnELNotFound() {
+        String result = null;
+
+        if (jspPropertyGroup.getErrorOnELNotFound() != null) {
+            result = jspPropertyGroup.getErrorOnELNotFound().toString();
+        }
+
+        return result;
+    }
+
+
+    @Override
     public String getErrorOnUndeclaredNamespace() {
         String result = null;
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java b/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java
index 2472aae..ba3bd53 100644
--- a/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java
+++ b/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java
@@ -283,6 +283,8 @@ public class WebRuleSet implements RuleSet {
                                "setDeferredSyntax", 0);
         digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/el-ignored",
                                "setElIgnored", 0);
+        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/error-on-el-not-found",
+                               "setErrorOnELNotFound", 0);
         digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-coda",
                                "addIncludeCoda", 0);
         digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-prelude",
diff --git a/test/org/apache/jasper/compiler/TestJspConfig.java b/test/org/apache/jasper/compiler/TestJspConfig.java
index 9389c2c..64f6d0c 100644
--- a/test/org/apache/jasper/compiler/TestJspConfig.java
+++ b/test/org/apache/jasper/compiler/TestJspConfig.java
@@ -18,6 +18,8 @@ package org.apache.jasper.compiler;
 
 import java.io.File;
 
+import jakarta.servlet.http.HttpServletResponse;
+
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -192,4 +194,147 @@ public class TestJspConfig extends TomcatBaseTest {
 
         Assert.assertTrue(result.indexOf("<p>00-hello world</p>") > 0);
     }
+
+    @Test
+    public void testErrorOnELNotFound01() throws Exception {
+        // Defaults
+
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp");
+        tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+
+        tomcat.start();
+
+        ByteChunk res = new ByteChunk();
+        int rc = getUrl("http://localhost:" + getPort() + "/test/jsp/errorOnELNotFound/default.jsp", res, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+
+        String result = res.toString();
+        Assert.assertTrue(result, result.indexOf("<p>00-OK</p>") > 0);
+    }
+
+    @Test
+    public void testErrorOnELNotFound02() throws Exception {
+        // Page directive true
+
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp");
+        tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+
+        tomcat.start();
+
+        ByteChunk res = new ByteChunk();
+        int rc = getUrl("http://localhost:" + getPort() + "/test/jsp/errorOnELNotFound/page-directive-true.jsp", res, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc);
+
+        // Look for the non-i18n part of the Exception message
+        String result = res.toString();
+        Assert.assertTrue(result, result.indexOf("[unknown]") > 0);
+    }
+
+    @Test
+    public void testErrorOnELNotFound03() throws Exception {
+        // Page directive false
+
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp");
+        tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+
+        tomcat.start();
+
+        ByteChunk res = new ByteChunk();
+        int rc = getUrl("http://localhost:" + getPort() + "/test/jsp/errorOnELNotFound/page-directive-false.jsp", res, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+
+        String result = res.toString();
+        Assert.assertTrue(result, result.indexOf("<p>00-OK</p>") > 0);
+    }
+
+    @Test
+    public void testErrorOnELNotFound04() throws Exception {
+        // web.xml true
+
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp");
+        tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+
+        tomcat.start();
+
+        ByteChunk res = new ByteChunk();
+        int rc = getUrl("http://localhost:" + getPort() + "/test/jsp/errorOnELNotFound/web-xml-true.jsp", res, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc);
+
+        // Look for the non-i18n part of the Exception message
+        String result = res.toString();
+        Assert.assertTrue(result, result.indexOf("[unknown]") > 0);
+    }
+
+    @Test
+    public void testErrorOnELNotFound05() throws Exception {
+        // web.xml false
+
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp");
+        tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+
+        tomcat.start();
+
+        ByteChunk res = new ByteChunk();
+        int rc = getUrl("http://localhost:" + getPort() + "/test/jsp/errorOnELNotFound/web-xml-false.jsp", res, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+
+        String result = res.toString();
+        Assert.assertTrue(result, result.indexOf("<p>00-OK</p>") > 0);
+    }
+
+    @Test
+    public void testErrorOnELNotFound06() throws Exception {
+        // tag file true
+
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp");
+        tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+
+        tomcat.start();
+
+        ByteChunk res = new ByteChunk();
+        int rc = getUrl("http://localhost:" + getPort() + "/test/jsp/errorOnELNotFound/tag-file-true.jsp", res, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc);
+
+        // Look for the non-i18n part of the Exception message
+        String result = res.toString();
+        Assert.assertTrue(result, result.indexOf("[unknown]") > 0);
+    }
+
+    @Test
+    public void testErrorOnELNotFound07() throws Exception {
+        // tag file false
+
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp");
+        tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+
+        tomcat.start();
+
+        ByteChunk res = new ByteChunk();
+        int rc = getUrl("http://localhost:" + getPort() + "/test/jsp/errorOnELNotFound/tag-file-false.jsp", res, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+
+        String result = res.toString();
+        Assert.assertTrue(result, result.indexOf("<p>00-OK</p>") > 0);
+    }
 }
diff --git a/test/org/apache/jasper/servlet/TestJspCServletContext.java b/test/org/apache/jasper/servlet/TestJspCServletContext.java
index f8d0adb..cc0032e 100644
--- a/test/org/apache/jasper/servlet/TestJspCServletContext.java
+++ b/test/org/apache/jasper/servlet/TestJspCServletContext.java
@@ -41,7 +41,7 @@ public class TestJspCServletContext {
         Assert.assertTrue(jspConfigDescriptor.getTaglibs().isEmpty());
         Collection<JspPropertyGroupDescriptor> propertyGroups =
                 jspConfigDescriptor.getJspPropertyGroups();
-        Assert.assertEquals(4, propertyGroups.size());
+        Assert.assertEquals(6, propertyGroups.size());
         Iterator<JspPropertyGroupDescriptor> groupIterator =
                 propertyGroups.iterator();
         JspPropertyGroupDescriptor groupDescriptor;
diff --git a/test/webapp/WEB-INF/tags/error-on-el-not-found-false.tag b/test/webapp/WEB-INF/tags/error-on-el-not-found-false.tag
new file mode 100644
index 0000000..8466d0e
--- /dev/null
+++ b/test/webapp/WEB-INF/tags/error-on-el-not-found-false.tag
@@ -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.
+--%>
+<%@ tag errorOnELNotFound="false" %>
+<p>00-O${unknown}K</p>
\ No newline at end of file
diff --git a/test/webapp/WEB-INF/tags/error-on-el-not-found-true.tag b/test/webapp/WEB-INF/tags/error-on-el-not-found-true.tag
new file mode 100644
index 0000000..ba23cac
--- /dev/null
+++ b/test/webapp/WEB-INF/tags/error-on-el-not-found-true.tag
@@ -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.
+--%>
+<%@ tag errorOnELNotFound="true" %>
+<p>00-O${unknown}K</p>
\ No newline at end of file
diff --git a/test/webapp/WEB-INF/web.xml b/test/webapp/WEB-INF/web.xml
index 0a28fc1..b1f30f0 100644
--- a/test/webapp/WEB-INF/web.xml
+++ b/test/webapp/WEB-INF/web.xml
@@ -152,6 +152,14 @@
       <page-encoding>ISO-8859-1</page-encoding>
       <is-xml>true</is-xml>
     </jsp-property-group>
+    <jsp-property-group>
+      <url-pattern>/jsp/errorOnELNotFound/web-xml-true.jsp</url-pattern>
+      <error-on-el-not-found>true</error-on-el-not-found>
+    </jsp-property-group>
+    <jsp-property-group>
+      <url-pattern>/jsp/errorOnELNotFound/web-xml-false.jsp</url-pattern>
+      <error-on-el-not-found>false</error-on-el-not-found>
+    </jsp-property-group>
   </jsp-config>
 
   <servlet>
diff --git a/test/webapp/jsp/errorOnELNotFound/default.jsp b/test/webapp/jsp/errorOnELNotFound/default.jsp
new file mode 100644
index 0000000..a3e66c9
--- /dev/null
+++ b/test/webapp/jsp/errorOnELNotFound/default.jsp
@@ -0,0 +1,21 @@
+<%--
+  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.
+--%>
+<html>
+  <body>
+    <p>00-O${unknown}K</p>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/jsp/errorOnELNotFound/page-directive-false.jsp b/test/webapp/jsp/errorOnELNotFound/page-directive-false.jsp
new file mode 100644
index 0000000..26ba5de
--- /dev/null
+++ b/test/webapp/jsp/errorOnELNotFound/page-directive-false.jsp
@@ -0,0 +1,22 @@
+<%--
+  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.
+--%>
+<%@ page errorOnELNotFound="false" %>
+<html>
+  <body>
+    <p>00-O${unknown}K</p>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/jsp/errorOnELNotFound/page-directive-true.jsp b/test/webapp/jsp/errorOnELNotFound/page-directive-true.jsp
new file mode 100644
index 0000000..fe241b0
--- /dev/null
+++ b/test/webapp/jsp/errorOnELNotFound/page-directive-true.jsp
@@ -0,0 +1,22 @@
+<%--
+  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.
+--%>
+<%@ page errorOnELNotFound="true" %>
+<html>
+  <body>
+    <p>00-O${unknown}K</p>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/jsp/errorOnELNotFound/tag-file-false.jsp b/test/webapp/jsp/errorOnELNotFound/tag-file-false.jsp
new file mode 100644
index 0000000..df20a0b
--- /dev/null
+++ b/test/webapp/jsp/errorOnELNotFound/tag-file-false.jsp
@@ -0,0 +1,23 @@
+<%--
+  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.
+--%>
+<%@ page errorOnELNotFound="true" %>
+<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
+<html>
+  <body>
+    <tags:error-on-el-not-found-false />
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/jsp/errorOnELNotFound/tag-file-true.jsp b/test/webapp/jsp/errorOnELNotFound/tag-file-true.jsp
new file mode 100644
index 0000000..84c9021
--- /dev/null
+++ b/test/webapp/jsp/errorOnELNotFound/tag-file-true.jsp
@@ -0,0 +1,23 @@
+<%--
+  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.
+--%>
+<%@ page errorOnELNotFound="false" %>
+<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
+<html>
+  <body>
+    <tags:error-on-el-not-found-true />
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/jsp/errorOnELNotFound/web-xml-false.jsp b/test/webapp/jsp/errorOnELNotFound/web-xml-false.jsp
new file mode 100644
index 0000000..a3e66c9
--- /dev/null
+++ b/test/webapp/jsp/errorOnELNotFound/web-xml-false.jsp
@@ -0,0 +1,21 @@
+<%--
+  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.
+--%>
+<html>
+  <body>
+    <p>00-O${unknown}K</p>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/jsp/errorOnELNotFound/web-xml-true.jsp b/test/webapp/jsp/errorOnELNotFound/web-xml-true.jsp
new file mode 100644
index 0000000..a3e66c9
--- /dev/null
+++ b/test/webapp/jsp/errorOnELNotFound/web-xml-true.jsp
@@ -0,0 +1,21 @@
+<%--
+  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.
+--%>
+<html>
+  <body>
+    <p>00-O${unknown}K</p>
+  </body>
+</html>
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 5aba0a1..efe4093 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -179,6 +179,11 @@
         rather than the types being passed explicitly to
         <code>ExpressionFactory.createMethodExpression()</code>. (markt)
       </fix>
+      <add>
+        Add support for a new page/tag directive <code>errorOnELNotFound</code>
+        that can be used to trigger an identifier if an EL expression in a
+        page/tag contains an identifier that cannot be resolved. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Web applications">

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org