This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit c7f47b0107543b9e7847885d7d667ac840b56887 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]
