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 9280097e62b619f92d9660fcaf31d53eed67c988 Author: opencode <[email protected]> AuthorDate: Sun Oct 25 23:00:52 2026 +0100 Rethrow message-less borrow failures from getConnectionAsync The empty-pool detection in the asynchronous borrow dereferenced the message of the caught SQLException without a null check, so a borrow failure that carried no message - which the pool itself manufactures when wrapping a non-SQL connector failure - surfaced to the caller as a NullPointerException instead of the original SQLException. A missing message is now treated as a missing "NoWait" marker, so the failure is rethrown intact. --- .../apache/tomcat/jdbc/pool/ConnectionPool.java | 7 ++- .../apache/tomcat/jdbc/pool/TestAsyncBorrow.java | 67 ++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java index 4565997419..5c2788819a 100644 --- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java +++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java @@ -179,7 +179,12 @@ public class ConnectionPool { return new ConnectionFuture(pc); } }catch (SQLException x) { - if (x.getMessage().indexOf("NoWait")<0) { + // JDBC exceptions may carry no message at all; a missing + // "NoWait" marker must be assumed for those, so that the + // original failure reaches the caller instead of a + // NullPointerException from dereferencing the message + String msg = x.getMessage(); + if (msg == null || msg.indexOf("NoWait")<0) { throw x; } } diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestAsyncBorrow.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestAsyncBorrow.java index dcb43154ad..a42db57d1d 100644 --- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestAsyncBorrow.java +++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestAsyncBorrow.java @@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; @@ -118,6 +119,72 @@ class TestAsyncBorrow extends JdbcPoolBaseTest { } } + @Test + void asyncBorrowFailureWithoutAMessageStillSurfacesAsSqlException() + throws Exception { + // A failing connector whose exception carries no message: the pool + // wraps it as a message-less SQLException, and the async borrow must + // rethrow that failure rather than dereference its missing message + DataSource ds = newDataSource(); + ds.getPoolProperties().setInitialSize(0); + ds.setDataSource(new javax.sql.DataSource() { + @Override + public Connection getConnection() { + throw new IllegalStateException(); + } + + @Override + public Connection getConnection(String user, String password) { + throw new IllegalStateException(); + } + + @Override + public java.io.PrintWriter getLogWriter() { + return null; + } + + @Override + public void setLogWriter(java.io.PrintWriter out) { + // no logging + } + + @Override + public void setLoginTimeout(int seconds) { + // no timeout + } + + @Override + public int getLoginTimeout() { + return 0; + } + + @Override + public java.util.logging.Logger getParentLogger() + throws java.sql.SQLFeatureNotSupportedException { + throw new java.sql.SQLFeatureNotSupportedException(); + } + + @Override + public <T> T unwrap(Class<T> iface) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isWrapperFor(Class<?> iface) { + return false; + } + }); + + SQLException failure = assertThrows(SQLException.class, + ds::getConnectionAsync, + "the borrow failure must reach the caller as a SQLException " + + "even when its message is null"); + assertNull(failure.getMessage(), + "the rethrown failure must keep its absent message"); + assertTrue(failure.getCause() instanceof IllegalStateException, + "the wrapped connector failure must be preserved as cause"); + } + @Test void concurrentAsyncBorrowersAreAllServed() throws Exception { final int borrowers = 6; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
