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 c166bc59f9b2cd5e4252daf2528660b26c8ba04e 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 21c6114589..a8bb7256b5 100644 --- a/java/org/apache/catalina/realm/JNDIRealm.java +++ b/java/org/apache/catalina/realm/JNDIRealm.java @@ -294,11 +294,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 */ @@ -2713,21 +2708,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)); } } @@ -2849,16 +2839,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); @@ -2868,9 +2860,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 e4dc641b61..1b465402f6 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -1893,6 +1893,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]
