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 b3682b7bd287e59dc2b38b3e20ff8a16fef156ad
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]

Reply via email to