This is an automated email from the ASF dual-hosted git repository. markt-asf pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit a6d57813e4a67c6cedac928afac1923a1b282679 Author: Arturo Bernal <[email protected]> AuthorDate: Fri Oct 23 14:07:55 2026 +0200 Fix BZ 69236 - reset JDBC interceptors in declaration order When a pooled connection was created, the interceptor chain was built from the last interceptor to the first and each interceptor was reset as it was added, so reset() was called in reverse order. For a connection taken from the pool the chain was reset from the first interceptor to the last. Build the chain first and then reset it in declaration order in both cases. --- .../apache/tomcat/jdbc/pool/ConnectionPool.java | 11 ++- .../jdbc/test/InterceptorResetOrderTest.java | 106 +++++++++++++++++++++ 2 files changed, 113 insertions(+), 4 deletions(-) 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 565b5e5543..e4141d9aef 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 @@ -309,8 +309,6 @@ public class ConnectionPool { interceptor.setProperties(proxies[i].getProperties()); //setup the chain interceptor.setNext(handler); - //call reset - interceptor.reset(this, con); //configure the last one to be held by the connection handler = interceptor; }catch(Exception x) { @@ -321,13 +319,18 @@ public class ConnectionPool { } //cache handler for the next iteration con.setHandler(handler); - } else { + } + //reset the chain in the order the interceptors are declared, for new and cached handlers alike + try { JdbcInterceptor next = handler; - //we have a cached handler, reset it while (next!=null) { next.reset(this, con); next = next.getNext(); } + }catch(Exception x) { + SQLException sx = new SQLException("Unable to reset interceptor chain."); + sx.initCause(x); + throw sx; } // setup statement proxy if (getPoolProperties().getUseStatementFacade()) { diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/InterceptorResetOrderTest.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/InterceptorResetOrderTest.java new file mode 100644 index 0000000000..a4824702cf --- /dev/null +++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/InterceptorResetOrderTest.java @@ -0,0 +1,106 @@ +/* + * 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.test; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import org.apache.tomcat.jdbc.pool.ConnectionPool; +import org.apache.tomcat.jdbc.pool.JdbcInterceptor; +import org.apache.tomcat.jdbc.pool.PooledConnection; +import org.apache.tomcat.jdbc.test.driver.Driver; + +/** + * The reset order of the interceptors must follow the order in which they are declared, whether the connection is new + * or is taken from the pool. + */ +public class InterceptorResetOrderTest extends DefaultTestCase { + + private static final List<String> RESETS = Collections.synchronizedList(new ArrayList<>()); + + @Before + public void setUp() { + RESETS.clear(); + datasource.setDriverClassName(Driver.class.getName()); + datasource.setUrl("jdbc:tomcat:test"); + datasource.setMaxActive(1); + datasource.setInitialSize(0); + datasource.setMinIdle(0); + datasource.setJdbcInterceptors(First.class.getName() + ";" + Second.class.getName()); + } + + @Test + public void testResetOrderOnNewConnection() throws SQLException { + datasource.getConnection().close(); + Assert.assertEquals(Arrays.asList("first", "second"), RESETS); + } + + @Test + public void testResetOrderOnReusedConnection() throws SQLException { + datasource.getConnection().close(); + RESETS.clear(); + datasource.getConnection().close(); + Assert.assertEquals(Arrays.asList("first", "second"), RESETS); + } + + @Test + public void testResetFailureIsReportedAsSQLException() { + datasource.setJdbcInterceptors(Failing.class.getName()); + + SQLException e = Assert.assertThrows(SQLException.class, () -> datasource.getConnection()); + + Assert.assertTrue(e.getCause() instanceof IllegalStateException); + } + + public static class Failing extends JdbcInterceptor { + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + if (parent != null) { + throw new IllegalStateException(); + } + } + } + + public static class First extends JdbcInterceptor { + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + // the pool also resets interceptors without a parent when a connection is discarded + if (parent != null) { + RESETS.add("first"); + } + } + } + + public static class Second extends JdbcInterceptor { + + @Override + public void reset(ConnectionPool parent, PooledConnection con) { + if (parent != null) { + RESETS.add("second"); + } + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
