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 4f332e90cfe4b0f6f031fc528c3316cbc48ff1df 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]
