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

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

commit ba79c3d10668278bca80301004a6f3b1191aeb96
Author: opencode <[email protected]>
AuthorDate: Fri Oct 30 21:48:11 2026 +0100

    Name the actual configuration in the XA backing-source refusal
    
    The refusal requireXABacking() raises when no backing data source is set
    asserted that the pool "is configured with a JDBC driver and URL (or the
    dataSourceJNDI lookup did not resolve to a data source)". Neither clause
    is necessarily true. A pool configured programmatically with only
    dataSourceJNDI hits this message - nothing outside DataSourceFactory ever
    resolves that name - and the message both claimed a driver/URL
    configuration that does not exist and never named the JNDI name that
    does. A pool with no configuration at all was told the same thing. And
    the parenthetical is dead: on the factory path an unresolvable name
    throws a NamingException before any XADataSource is constructed, while a
    lookup that resolves to a non-DataSource object leaves the data source
    non-null and reaches the other, accurate refusal.
    
    The message is now composed from the actual configuration: when a
    dataSourceJNDI name is set it is named, with an explanation that only the
    factory resolves names and programmatic configurations must set the
    dataSource attribute; otherwise the message states that neither attribute
    is set and that only plain driver/URL connections can be pooled. The
    refusal itself, its exception type and the wrong-type message are
    unchanged.
    
    Testing: TestXADataSourceRefusalMessages pins that the JNDI-case refusal
    names the configured JNDI name and asserts no absent driver/URL
    configuration, and that the unconfigured refusal names both missing
    attributes; both fail against the previous message.
---
 .../org/apache/tomcat/jdbc/pool/XADataSource.java  | 18 ++++-
 .../jdbc/pool/TestXADataSourceRefusalMessages.java | 80 ++++++++++++++++++++++
 2 files changed, 95 insertions(+), 3 deletions(-)

diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java
index 9265b35c9f..15120840db 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java
@@ -87,9 +87,21 @@ public class XADataSource extends DataSource implements 
javax.sql.XADataSource {
             return;
         }
         if (ds == null) {
-            throw new SQLException("The connection pool [" + getName() + "] 
cannot provide XA connections: it is" +
-                    " configured with a JDBC driver and URL (or the 
'dataSourceJNDI' lookup did not resolve to a" +
-                    " data source), so the pool holds plain JDBC connections. 
Set the 'dataSource' or" +
+            // A configured JNDI name only reaches the pool as a data source
+            // through the DataSourceFactory, which fails pool creation when
+            // the name cannot be resolved; a name set on a programmatically
+            // configured pool therefore never becomes a data source at all.
+            String jndiName = getPoolProperties().getDataSourceJNDI();
+            if (jndiName != null) {
+                throw new SQLException("The connection pool [" + getName() + 
"] cannot provide XA connections:" +
+                        " the name \"" + jndiName + "\" set through the 
'dataSourceJNDI' attribute has not been" +
+                        " resolved into a data source; the name is only looked 
up when the pool is created" +
+                        " through the DataSourceFactory. Set the 'dataSource' 
attribute to a" +
+                        " javax.sql.XADataSource provided by the JDBC 
driver.");
+            }
+            throw new SQLException("The connection pool [" + getName() + "] 
cannot provide XA connections:" +
+                    " neither the 'dataSource' nor the 'dataSourceJNDI' 
attribute is set, so only plain JDBC" +
+                    " connections from a JDBC driver and URL can be pooled. 
Set the 'dataSource' or" +
                     " 'dataSourceJNDI' attribute to a javax.sql.XADataSource 
provided by the JDBC driver.");
         }
         throw new SQLException("The connection pool [" + getName() + "] cannot 
provide XA connections: the data" +
diff --git 
a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestXADataSourceRefusalMessages.java
 
b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestXADataSourceRefusalMessages.java
new file mode 100644
index 0000000000..495f5be5c6
--- /dev/null
+++ 
b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestXADataSourceRefusalMessages.java
@@ -0,0 +1,80 @@
+/*
+ *  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.tomcat.jdbc.pool;
+
+import java.sql.SQLException;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * The refusal an {@link XADataSource} gives when it has no XA-capable
+ * backing must describe what the pool is actually configured with, not
+ * assert a configuration that may not exist. Two configurations reach the
+ * no-backing refusal: a programmatically set dataSourceJNDI name - the
+ * pool never resolves such a name itself, the lookup only happens in the
+ * DataSourceFactory, which fails the pool creation outright when the name
+ * cannot be resolved - and a pool with neither dataSource attribute set.
+ * The refusal needs no database: the guard fires before the pool is
+ * touched.
+ */
+class TestXADataSourceRefusalMessages {
+
+    @Test
+    void unresolvedDataSourceJndiRefusalNamesTheConfiguredJndiName() {
+        // Programmatic configuration: only a 'dataSourceJNDI' name is set.
+        // Nothing outside the DataSourceFactory resolves the name, so the
+        // pool has no backing data source and the refusal must name the
+        // actually-configured JNDI name instead of pointing at a
+        // driver/URL configuration this pool does not have.
+        XADataSource ds = new XADataSource();
+        ds.getPoolProperties().setDataSourceJNDI("jdbc/XaTestDataSource");
+
+        SQLException e = Assertions.assertThrows(SQLException.class, 
ds::getXAConnection,
+                "an XA pool whose JNDI name was never resolved must refuse XA 
connections");
+        String message = e.getMessage();
+        Assertions.assertTrue(message.contains("jdbc/XaTestDataSource"),
+                "failure must name the configured 'dataSourceJNDI' name, was: 
" + message);
+        Assertions.assertFalse(message.contains("URL"),
+                "failure must not assert a driver/URL configuration, was: " + 
message);
+        Assertions.assertFalse(message.contains("configured with"),
+                "failure must not assert a configuration the pool does not 
have, was: " + message);
+
+        // The credential variant must refuse with the same message.
+        SQLException e2 = Assertions.assertThrows(SQLException.class, () -> 
ds.getXAConnection("sa", ""),
+                "the credential variant must refuse the same way");
+        Assertions.assertEquals(message, e2.getMessage());
+    }
+
+    @Test
+    void 
poolWithoutDataSourceOrJndiRefusalStatesMissingDataSourceConfiguration() {
+        // Neither 'dataSource' nor 'dataSourceJNDI' is set: the refusal must
+        // say so rather than claim the pool is configured with a JDBC driver
+        // and URL, which this pool does not have either.
+        XADataSource ds = new XADataSource();
+
+        SQLException e = Assertions.assertThrows(SQLException.class, 
ds::getXAConnection,
+                "an XA pool without any backing data source configuration must 
refuse XA connections");
+        String message = e.getMessage();
+        Assertions.assertTrue(message.contains("'dataSource'"),
+                "failure must name the missing 'dataSource' attribute, was: " 
+ message);
+        Assertions.assertTrue(message.contains("'dataSourceJNDI'"),
+                "failure must name the missing 'dataSourceJNDI' attribute, 
was: " + message);
+        Assertions.assertFalse(message.contains("configured with"),
+                "failure must not assert a configuration the pool does not 
have, was: " + message);
+    }
+}


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

Reply via email to