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

rmaucher pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 37a1db8eb7a4bdba7b53482a782dc159ad82f58b
Author: opencode <[email protected]>
AuthorDate: Mon Oct 19 10:33:12 2026 +0200

    Report bean getter and setter exceptions in GraalVM mode
    
    GraalBeanELResolver caught exceptions raised while invoking a bean
    getter or setter, passed them through ExceptionUtils.handleThrowable()
    which only rethrows VirtualMachineError, and then returned as if the
    property had been resolved successfully. A throwing getter therefore
    yielded a null value and a throwing setter a silently dropped write.
    The ELResolver contract, and the standard BeanELResolver used outside
    GraalVM native image mode, wrap such failures in an ELException.
    
    Rethrow the unwrapped cause as an ELException after handleThrowable(),
    mirroring BeanELResolver so a native image build reports the same EL
    errors as a standard JVM. Test with bean properties whose accessor and
    mutator throw.
    
    Found by code review.
---
 java/org/apache/jasper/el/JasperELResolver.java    | 12 +++++---
 .../org/apache/jasper/el/TestJasperELResolver.java | 33 ++++++++++++++++++++++
 2 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/java/org/apache/jasper/el/JasperELResolver.java 
b/java/org/apache/jasper/el/JasperELResolver.java
index 8fb27a8d77..ae13dcdeb1 100644
--- a/java/org/apache/jasper/el/JasperELResolver.java
+++ b/java/org/apache/jasper/el/JasperELResolver.java
@@ -218,8 +218,7 @@ public class JasperELResolver extends CompositeELResolver {
                     method.setAccessible(true);
                     value = method.invoke(base, (Object[]) null);
                 } catch (Exception e) {
-                    Throwable thr = 
ExceptionUtils.unwrapInvocationTargetException(e);
-                    ExceptionUtils.handleThrowable(thr);
+                    throw toELException(e);
                 }
             }
             return value;
@@ -241,8 +240,7 @@ public class JasperELResolver extends CompositeELResolver {
                     method.setAccessible(true);
                     method.invoke(base, value);
                 } catch (Exception e) {
-                    Throwable thr = 
ExceptionUtils.unwrapInvocationTargetException(e);
-                    ExceptionUtils.handleThrowable(thr);
+                    throw toELException(e);
                 }
             }
         }
@@ -287,6 +285,12 @@ public class JasperELResolver extends CompositeELResolver {
             return null;
         }
 
+        private static ELException toELException(Exception e) {
+            Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e);
+            ExceptionUtils.handleThrowable(thr);
+            return new ELException(thr);
+        }
+
         private static String capitalize(String name) {
             if (name == null || name.isEmpty()) {
                 return name;
diff --git a/test/org/apache/jasper/el/TestJasperELResolver.java 
b/test/org/apache/jasper/el/TestJasperELResolver.java
index a6f202c36c..8343241fd0 100644
--- a/test/org/apache/jasper/el/TestJasperELResolver.java
+++ b/test/org/apache/jasper/el/TestJasperELResolver.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import jakarta.el.ELContext;
+import jakarta.el.ELException;
 import jakarta.el.ELResolver;
 import jakarta.servlet.jsp.el.ImplicitObjectELResolver;
 
@@ -111,6 +112,32 @@ public class TestJasperELResolver {
         resolver.setValue(context, bean, "foo", Integer.valueOf(7));
         Assert.assertFalse("A mismatched value must leave the property 
unresolved",
                 context.isPropertyResolved());
+
+        // An exception thrown by a getter must be reported rather than
+        // swallowed. Before the fix it produced a silently resolved null.
+        context.setPropertyResolved(false);
+        try {
+            resolver.getValue(context, bean, "failing");
+            Assert.fail("A getter exception must be reported");
+        } catch (ELException e) {
+            Assert.assertTrue("The reported cause must be the thrown 
exception",
+                    e.getCause() instanceof IllegalStateException);
+            Assert.assertEquals("getter failed", e.getCause().getMessage());
+            Assert.assertTrue("A found property should be marked resolved", 
context.isPropertyResolved());
+        }
+
+        // An exception thrown by a setter must be reported rather than
+        // swallowed. Before the fix the failed write went unnoticed.
+        context.setPropertyResolved(false);
+        try {
+            resolver.setValue(context, bean, "failing", "some value");
+            Assert.fail("A setter exception must be reported");
+        } catch (ELException e) {
+            Assert.assertTrue("The reported cause must be the thrown 
exception",
+                    e.getCause() instanceof IllegalStateException);
+            Assert.assertEquals("setter failed", e.getCause().getMessage());
+            Assert.assertTrue("A found property should be marked resolved", 
context.isPropertyResolved());
+        }
     }
 
     public static class TestBean {
@@ -140,5 +167,11 @@ public class TestJasperELResolver {
         public Object getLastBla() {
             return bla;
         }
+        public String getFailing() {
+            throw new IllegalStateException("getter failed");
+        }
+        public void setFailing(@SuppressWarnings("unused") String value) {
+            throw new IllegalStateException("setter failed");
+        }
     }
 }


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

Reply via email to