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

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

commit a14f643ee79a22e0ad82515b10a345c6bd52eb75
Author: opencode <[email protected]>
AuthorDate: Sun Oct 25 17:55:39 2026 +0100

    Fail the naming factory with NamingExceptions when the reference type is 
unusable
    
    GenericNamingResourcesFactory used the reference class name, the
    "type" attribute of a <Resource>, without validation. Nothing requires
    the type attribute, so a reference could carry a null class name and the
    factory then failed with a bare NullPointerException from Class.forName,
    or with an empty name with a ClassNotFoundException naming nothing, or
    with a bare NoSuchMethodException when the type named an interface or a
    class without a public no-argument constructor, the natural thing to
    write being type="javax.sql.DataSource". None of these diagnostics
    named the resource or explained what was wrong with its configuration.
    
    Validate the class name and translate the failure modes into
    NamingExceptions that name the reference and the class, following the
    container's BeanFactory: a missing or empty type reports that the type
    attribute is required, a class that cannot be loaded reports that it was
    not found, a class with no usable public no-argument constructor,
    including interfaces and abstract classes, reports that it cannot be
    created by the factory, and a constructor that throws reports that,
    while still propagating a fatal VirtualMachineError. As before, input
    that is not a Reference returns null.
    
    Add a test covering the null type, an unknown class, an interface and a
    throwing constructor.
---
 .../jdbc/naming/GenericNamingResourcesFactory.java | 49 ++++++++++++++++++--
 .../tomcat/jdbc/naming/TestNamingFactory.java      | 53 ++++++++++++++++++++++
 2 files changed, 97 insertions(+), 5 deletions(-)

diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java
index b265c2a8c6..f0dfa54a31 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java
@@ -29,6 +29,7 @@ import java.util.Set;
 
 import javax.naming.Context;
 import javax.naming.Name;
+import javax.naming.NamingException;
 import javax.naming.RefAddr;
 import javax.naming.Reference;
 import javax.naming.spi.ObjectFactory;
@@ -81,11 +82,49 @@ public class GenericNamingResourcesFactory implements 
ObjectFactory {
         Enumeration<RefAddr> refs = ref.getAll();
 
         String type = ref.getClassName();
-        Object o =
-                ClassLoaderUtil.loadClass(
-                type,
-                GenericNamingResourcesFactory.class.getClassLoader(),
-                
Thread.currentThread().getContextClassLoader()).getConstructor().newInstance();
+        if (type == null || type.isEmpty()) {
+            throw new NamingException(
+                    "No resource type (the \"type\" attribute) is defined for 
reference ["
+                            + name + "]; GenericNamingResourcesFactory needs 
the class of the "
+                            + "object to create.");
+        }
+        Object o;
+        try {
+            Class<?> clazz = ClassLoaderUtil.loadClass(
+                    type,
+                    GenericNamingResourcesFactory.class.getClassLoader(),
+                    Thread.currentThread().getContextClassLoader());
+            o = clazz.getConstructor().newInstance();
+        } catch (ClassNotFoundException e) {
+            NamingException ne = new NamingException(
+                    "Class [" + type + "] of reference [" + name + "] was not 
found");
+            ne.setRootCause(e);
+            throw ne;
+        } catch (InvocationTargetException e) {
+            // The class has a no-argument constructor, but it threw. Propagate
+            // only a fatal VirtualMachineError, in the spirit of
+            // ExceptionUtils.handleThrowable(), which tomcat-jdbc.jar cannot
+            // use directly.
+            Throwable cause = e.getCause();
+            if (cause instanceof VirtualMachineError) {
+                throw (VirtualMachineError) cause;
+            }
+            NamingException ne = new NamingException("The constructor of class 
["
+                    + type + "] of reference [" + name
+                    + "] threw an exception and the object cannot be created");
+            ne.setRootCause(e);
+            throw ne;
+        } catch (ReflectiveOperationException e) {
+            // No public no-arg constructor (NoSuchMethodException), or the
+            // class cannot be instantiated (abstract, interface, illegal
+            // access).
+            NamingException ne = new NamingException("Class [" + type
+                    + "] of reference [" + name
+                    + "] has no usable public no-argument constructor and "
+                    + "cannot be created by this factory");
+            ne.setRootCause(e);
+            throw ne;
+        }
 
         while (refs.hasMoreElements()) {
             RefAddr addr = refs.nextElement();
diff --git 
a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/naming/TestNamingFactory.java
 
b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/naming/TestNamingFactory.java
index 860eabb00f..d8ab28892a 100644
--- 
a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/naming/TestNamingFactory.java
+++ 
b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/naming/TestNamingFactory.java
@@ -25,12 +25,14 @@ import java.util.logging.Logger;
 
 import javax.naming.CompositeName;
 import javax.naming.Name;
+import javax.naming.NamingException;
 import javax.naming.Reference;
 import javax.naming.StringRefAddr;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import org.junit.jupiter.api.Test;
 
@@ -306,6 +308,57 @@ class TestNamingFactory extends JdbcPoolBaseTest {
         }
     }
 
+    @Test
+    void unusableClassNamesFailWithNamingExceptions() throws Exception {
+        GenericNamingResourcesFactory factory =
+                new GenericNamingResourcesFactory();
+        Name refName = new CompositeName("jdbc/test");
+
+        // missing type attribute: a ResourceRef may carry a null class name
+        NamingException ne = assertThrows(NamingException.class,
+                () -> factory.getObjectInstance(
+                        new Reference(null, null, null), refName, null, null));
+        assertTrue(ne.getMessage().contains("type"),
+                "message must name the missing type attribute");
+
+        // unknown class: a NamingException naming the class, not a bare
+        // ClassNotFoundException with an empty name
+        ne = assertThrows(NamingException.class,
+                () -> factory.getObjectInstance(new Reference(
+                        "com.example.NoSuchClass", null, null),
+                        refName, null, null));
+        assertTrue(ne.getMessage().contains("com.example.NoSuchClass"),
+                "message must name the missing class");
+        assertInstanceOf(ClassNotFoundException.class, ne.getRootCause());
+
+        // interface: a NamingException naming the class, not a bare
+        // NoSuchMethodException
+        ne = assertThrows(NamingException.class,
+                () -> factory.getObjectInstance(new Reference(
+                        "javax.sql.DataSource", null, null),
+                        refName, null, null));
+        assertTrue(ne.getMessage().contains("javax.sql.DataSource"),
+                "message must name the unusable class");
+        assertInstanceOf(ReflectiveOperationException.class,
+                ne.getRootCause());
+        // a class whose no-argument constructor exists but throws
+        ne = assertThrows(NamingException.class,
+                () -> factory.getObjectInstance(new Reference(
+                        ThrowingCtorTarget.class.getName(), null, null),
+                        refName, null, null));
+        assertTrue(ne.getMessage().contains("constructor"),
+                "message must report the failing constructor");
+        assertInstanceOf(java.lang.reflect.InvocationTargetException.class,
+                ne.getRootCause());
+    }
+
+    /** Target whose no-argument constructor throws. */
+    public static class ThrowingCtorTarget {
+        public ThrowingCtorTarget() {
+            throw new IllegalStateException("boom");
+        }
+    }
+
     /** Plain configuration target for the generic naming factory. */
     public static class NamingTarget {
         private String name;


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

Reply via email to