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 758e9b6a4b46991a081338006dd7cf8e02a90d77
Author: opencode <[email protected]>
AuthorDate: Thu Oct 29 23:47:25 2026 +0100

    PoolProperties: make the interceptor definition cache immune to lost 
invalidation
    
    getJdbcInterceptorsAsArray() checked, parsed and published the volatile
    interceptors cache without a lock while setJdbcInterceptors() swapped
    the string and cleared the cache, also without a lock. A getter that
    read the old string before a reconfiguration could store its
    already-parsed array after the setter cleared the cache, and from then
    on every new connection silently used the discarded interceptor chain
    until someone re-set the property. The getter and setter are now both
    synchronized on the PoolProperties instance - the same lock the
    dbProperties pair uses - so a parse that started from the previous
    value of the string can never undo a later clear. The getter also reads
    the reconfigurable string once so a parse works on a single snapshot.
    Interceptor classes are still loaded by the pool outside the lock.
---
 .../apache/tomcat/jdbc/pool/PoolProperties.java    | 37 +++++++++++++++++++---
 1 file changed, 33 insertions(+), 4 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 b42c98e6ab..1782e75782 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
@@ -424,13 +424,32 @@ public class PoolProperties implements PoolConfiguration, 
Cloneable, Serializabl
     }
 
 
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The string is parsed on first use and the result cached. The check,
+     * the parse and the publish run inside this one synchronized method, under
+     * the same lock {@link #setJdbcInterceptors(String)} takes to clear the
+     * cache: a parse that read the previous value of the string can therefore
+     * never store its result back after a reconfiguration cleared it, which is
+     * what would leave every later caller on the discarded interceptor chain
+     * for good. The string is read once into a local so the parse works on a
+     * single snapshot. A malformed value throws before the cache is written, 
so
+     * a later call parses the then-current value again. The lock is never held
+     * 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>
+     */
     @Override
-    public InterceptorDefinition[] getJdbcInterceptorsAsArray() {
+    public synchronized InterceptorDefinition[] getJdbcInterceptorsAsArray() {
         if (interceptors == null) {
-            if (jdbcInterceptors == null) {
+            // One read of the reconfigurable string: the parse below must see
+            // a single value, not a fresh read per use.
+            String interceptorList = jdbcInterceptors;
+            if (interceptorList == null) {
                 interceptors = new InterceptorDefinition[0];
             } else {
-                String[] interceptorValues = jdbcInterceptors.split(";");
+                String[] interceptorValues = interceptorList.split(";");
                 InterceptorDefinition[] definitions = new 
InterceptorDefinition[interceptorValues.length + 1];
                 // Always add the trap interceptor to the mix
                 definitions[0] = new 
InterceptorDefinition(TrapException.class);
@@ -729,8 +748,18 @@ public class PoolProperties implements PoolConfiguration, 
Cloneable, Serializabl
     }
 
 
+    /**
+     * Stores the interceptor configuration string and drops the parsed cache
+     * so the next {@link #getJdbcInterceptorsAsArray()} re-parses the new
+     * value. The clear runs under the same lock the parse publishes under, so
+     * a parse that started from the previous value of the string can never
+     * store its result back after this clear.
+     *
+     * @param jdbcInterceptors the semicolon separated interceptor 
configuration,
+     *                         may be {@code null} to use none
+     */
     @Override
-    public void setJdbcInterceptors(String jdbcInterceptors) {
+    public synchronized void setJdbcInterceptors(String jdbcInterceptors) {
         this.jdbcInterceptors = jdbcInterceptors;
         this.interceptors = null;
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to