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 04d1349aa53bfcefa352412e263de9e70eedc765
Author: opencode <[email protected]>
AuthorDate: Wed Oct 7 15:52:15 2026 +0200

    Make JNDIRealm URL failover decision per connection attempt
    
    open() signalled the failover retry by setting the realm-wide,
    non-volatile connectionAttempt field, which
    getDirectoryContextEnvironment() read to select between connectionURL
    and alternateURL. With connection pooling enabled, open() runs
    concurrently on different connections without any realm-level
    serialization: while one thread was failing over, another thread
    building its first-attempt environment could read the flag and connect
    to the alternate URL, and the reset in the finally block could send a
    concurrent thread's failover retry back to the primary that had just
    failed. Visibility of the plain int was not guaranteed either.
    
    The flag's meaningful lifetime is a single open() call, so remove the
    shared field entirely and pass an explicit useAlternateURL parameter to
    getDirectoryContextEnvironment(). This removes a protected field and
    changes a protected method signature. Neither was referenced outside
    JNDIRealm. Include a changelog entry.
---
 java/org/apache/catalina/realm/JNDIRealm.java | 26 +++++++++-----------------
 webapps/docs/changelog.xml                    |  9 +++++++++
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/java/org/apache/catalina/realm/JNDIRealm.java 
b/java/org/apache/catalina/realm/JNDIRealm.java
index 200b8ca434..720af96c5a 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -296,11 +296,6 @@ public class JNDIRealm extends RealmBase {
      */
     protected String alternateURL;
 
-    /**
-     * The number of connection attempts. If greater than zero we use the 
alternate url.
-     */
-    protected int connectionAttempt = 0;
-
     /**
      * Add this role to every authenticated user
      */
@@ -2694,21 +2689,16 @@ public class JNDIRealm extends RealmBase {
     protected void open(JNDIConnection connection) throws NamingException {
         try {
             // Ensure that we have a directory context available
-            connection.context = createDirContext(connection, 
getDirectoryContextEnvironment());
+            connection.context = createDirContext(connection, 
getDirectoryContextEnvironment(false));
         } catch (Exception e) {
             if (alternateURL == null || alternateURL.isEmpty()) {
                 // No alternate URL. Re-throw the exception.
                 throw e;
             }
-            connectionAttempt = 1;
             // log the first exception.
             containerLog.info(sm.getString("jndiRealm.exception.retry"), e);
             // Try connecting to the alternate url.
-            connection.context = createDirContext(connection, 
getDirectoryContextEnvironment());
-        } finally {
-            // reset it in case the connection times out.
-            // the primary may come back.
-            connectionAttempt = 0;
+            connection.context = createDirContext(connection, 
getDirectoryContextEnvironment(true));
         }
     }
 
@@ -2830,16 +2820,18 @@ public class JNDIRealm extends RealmBase {
     /**
      * Create our directory context configuration.
      *
+     * @param useAlternateURL Whether the alternate URL should be used in 
place of the connection URL
+     *
      * @return java.util.Hashtable the configuration for the directory context.
      */
-    protected Hashtable<String,String> getDirectoryContextEnvironment() {
+    protected Hashtable<String,String> getDirectoryContextEnvironment(boolean 
useAlternateURL) {
 
         Hashtable<String,String> env = new Hashtable<>();
 
         // Configure our directory context environment.
-        if (containerLog.isTraceEnabled() && connectionAttempt == 0) {
+        if (containerLog.isTraceEnabled() && !useAlternateURL) {
             containerLog.trace("Connecting to URL " + connectionURL);
-        } else if (containerLog.isTraceEnabled() && connectionAttempt > 0) {
+        } else if (containerLog.isTraceEnabled() && useAlternateURL) {
             containerLog.trace("Connecting to URL " + alternateURL);
         }
         env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
@@ -2849,9 +2841,9 @@ public class JNDIRealm extends RealmBase {
         if (connectionPassword != null) {
             env.put(Context.SECURITY_CREDENTIALS, connectionPassword);
         }
-        if (connectionURL != null && connectionAttempt == 0) {
+        if (connectionURL != null && !useAlternateURL) {
             env.put(Context.PROVIDER_URL, connectionURL);
-        } else if (alternateURL != null && connectionAttempt > 0) {
+        } else if (alternateURL != null && useAlternateURL) {
             env.put(Context.PROVIDER_URL, alternateURL);
         }
         if (authentication != null) {
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index c69484dca3..c0c9b1aa85 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -1884,6 +1884,15 @@
         no longer terminates the TLS session of another, still active pooled
         connection. (remm)
       </fix>
+      <fix>
+        Select the primary or alternate directory server URL for each
+        <code>JNDIRealm</code> connection attempt individually rather than via
+        a realm-wide mutable flag that concurrent pooled connections raced on,
+        which could transiently cause a connection to use the wrong URL. Note
+        that this removes the <code>connectionAttempt</code> protected field
+        and changes the signature of the protected
+        <code>getDirectoryContextEnvironment()</code> method. (remm)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">


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

Reply via email to