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 7e6700da108b051cbf2797402f26f9fe9f5b8fd4 Author: opencode <[email protected]> AuthorDate: Thu Nov 5 14:21:48 2026 +0100 Return a copy of the configured interceptor array PoolProperties.getJdbcInterceptorsAsArray() returned the cached InterceptorDefinition[] directly. A caller or tool that replaced or reordered an element silently changed the interceptor chain built for later connections. Return a clone of the array and document that the definitions it holds are shared and must not be mutated. Add a regression test that mutates the returned array. --- .../org/apache/tomcat/jdbc/pool/PoolProperties.java | 18 +++++++++++++----- .../apache/tomcat/jdbc/pool/TestPoolProperties.java | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java index ecaa1802c1..eb648ec43c 100644 --- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java +++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java @@ -465,6 +465,10 @@ public class PoolProperties implements PoolConfiguration, Cloneable, Serializabl * over interceptor class loading: an {@link InterceptorDefinition} parsed * from the string only keeps the class name, and those classes are loaded * later by the pool, outside this lock.</p> + * + * <p>The returned array is a copy, so a caller cannot change the cached + * definition array or the chain later connections build. The definitions + * it holds are shared and must be treated as read-only.</p> */ @Override public synchronized InterceptorDefinition[] getJdbcInterceptorsAsArray() { @@ -532,13 +536,12 @@ public class PoolProperties implements PoolConfiguration, Cloneable, Serializabl interceptors = definitions.toArray(new InterceptorDefinition[0]); } } - return interceptors; + return interceptors.clone(); } @Override public void setAccessToUnderlyingConnectionAllowed(boolean accessToUnderlyingConnectionAllowed) { - // NOOP } @@ -951,7 +954,8 @@ public class PoolProperties implements PoolConfiguration, Cloneable, Serializabl * credentials an operator normally configures rather than proving the * configuration holds no other secret. A property whose getter fails is * reported as an error in its own field and does not truncate the rest of - * the dump. + * the dump. {@code object_name} has no getter of its own: the factory + * applies it to the pool name, which the dump shows as {@code name}. * * @return the dump, always bracket-balanced even on internal failure */ @@ -1672,11 +1676,14 @@ public class PoolProperties implements PoolConfiguration, Cloneable, Serializabl } /** - * Returns the interceptor class, loading it if necessary. + * Returns the interceptor class, loading it if necessary. The configured class must extend + * {@link JdbcInterceptor}: a class that loads but does not extend it fails the pool start with a + * {@link ClassCastException} when this method returns. * * @return the interceptor class * * @throws ClassNotFoundException if the class cannot be found + * @throws ClassCastException if the class does not extend {@link JdbcInterceptor} */ @SuppressWarnings("unchecked") public Class<? extends JdbcInterceptor> getInterceptorClass() throws ClassNotFoundException { @@ -1700,7 +1707,8 @@ public class PoolProperties implements PoolConfiguration, Cloneable, Serializabl } /** - * Represents a property for a JDBC interceptor. + * Represents a property for a JDBC interceptor. Equality and hashing use the property name only, because the name + * keys the property in the definitions' map; two properties with the same name but different values compare equal. */ public static class InterceptorProperty implements Serializable { private static final long serialVersionUID = 1L; diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestPoolProperties.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestPoolProperties.java index 3c2cceb85a..087be04a3c 100644 --- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestPoolProperties.java +++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/TestPoolProperties.java @@ -170,6 +170,22 @@ class TestPoolProperties { "the dump must still identify the configured data source by class"); } + @Test + void getJdbcInterceptorsAsArrayReturnsACopy() { + PoolProperties properties = new PoolProperties(); + properties.setJdbcInterceptors("ResetAbandonedTimer"); + + PoolProperties.InterceptorDefinition[] first = properties.getJdbcInterceptorsAsArray(); + Assertions.assertTrue(first.length > 0, "precondition: the definition must parse"); + first[0] = null; + + PoolProperties.InterceptorDefinition[] second = properties.getJdbcInterceptorsAsArray(); + Assertions.assertNotSame(first, second, + "each call must return its own array"); + Assertions.assertNotNull(second[0], + "mutating the returned array must not change the cached definitions"); + } + @Test void setConnectionPropertiesRoundTripsThroughTheFactory() { Properties attrs = new Properties(); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
