This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new 5d2be5745d Fix a race with two requests looking up a singleton JNDI 
resource
5d2be5745d is described below

commit 5d2be5745dcb264ad4ba46a49f5b716b0afef63e
Author: Mark Thomas <[email protected]>
AuthorDate: Fri Jun 19 13:35:49 2026 +0100

    Fix a race with two requests looking up a singleton JNDI resource
---
 java/org/apache/naming/NamingContext.java | 58 ++++++++++++++++++++-----------
 webapps/docs/changelog.xml                |  8 +++++
 2 files changed, 46 insertions(+), 20 deletions(-)

diff --git a/java/org/apache/naming/NamingContext.java 
b/java/org/apache/naming/NamingContext.java
index 7f680a43d3..5da0f2d522 100644
--- a/java/org/apache/naming/NamingContext.java
+++ b/java/org/apache/naming/NamingContext.java
@@ -518,28 +518,28 @@ public class NamingContext implements Context {
             } else if (entry.type == NamingEntry.REFERENCE) {
                 try {
                     Object obj = null;
-                    if (!GRAAL) {
-                        obj = NamingManager.getObjectInstance(entry.value, 
name, this, env);
-                    } else {
-                        // NamingManager.getObjectInstance would simply return 
the reference here
-                        // Use the configured object factory to resolve it 
directly if possible
-                        // Note: This may need manual constructor reflection 
configuration
-                        Reference reference = (Reference) entry.value;
-                        String factoryClassName = 
reference.getFactoryClassName();
-                        if (factoryClassName != null) {
-                            Class<?> factoryClass = 
getClass().getClassLoader().loadClass(factoryClassName);
-                            ObjectFactory factory = (ObjectFactory) 
factoryClass.getDeclaredConstructor().newInstance();
-                            obj = factory.getObjectInstance(entry.value, name, 
this, env);
-                        }
-                    }
+                    boolean singleton = false;
                     if (entry.value instanceof ResourceRef) {
-                        boolean singleton = Boolean.parseBoolean(
-                                (String) ((ResourceRef) 
entry.value).get(ResourceRef.SINGLETON).getContent());
-                        if (singleton) {
-                            entry.type = NamingEntry.ENTRY;
-                            entry.value = obj;
+                        // Only create singleton instances inside the sync
+                        synchronized (entry) {
+                            if (entry.value instanceof ResourceRef) {
+                                singleton = Boolean.parseBoolean(
+                                        (String) ((ResourceRef) 
entry.value).get(ResourceRef.SINGLETON).getContent());
+                                if (singleton) {
+                                    obj = getObjectInstance(name, entry);
+                                    entry.type = NamingEntry.ENTRY;
+                                    entry.value = obj;
+                                }
+                            } else {
+                                // Another thread has created the singleton
+                                singleton = true;
+                                obj = entry.value;
+                            }
                         }
                     }
+                    if (!singleton) {
+                        obj = getObjectInstance(name, entry);
+                    }
                     if (obj == null) {
                         throw new 
NamingException(sm.getString("namingContext.failResolvingReference", name));
                     }
@@ -557,10 +557,28 @@ public class NamingContext implements Context {
                 return entry.value;
             }
         }
-
     }
 
 
+    private Object getObjectInstance(Name name, NamingEntry entry) throws 
Exception {
+        Object obj = null;
+        if (!GRAAL) {
+            obj = NamingManager.getObjectInstance(entry.value, name, this, 
env);
+        } else {
+            // NamingManager.getObjectInstance would simply return the 
reference here
+            // Use the configured object factory to resolve it directly if 
possible
+            // Note: This may need manual constructor reflection configuration
+            Reference reference = (Reference) entry.value;
+            String factoryClassName = reference.getFactoryClassName();
+            if (factoryClassName != null) {
+                Class<?> factoryClass = 
getClass().getClassLoader().loadClass(factoryClassName);
+                ObjectFactory factory = (ObjectFactory) 
factoryClass.getDeclaredConstructor().newInstance();
+                obj = factory.getObjectInstance(entry.value, name, this, env);
+            }
+        }
+        return obj;
+    }
+
     /**
      * Binds a name to an object. All intermediate contexts and the target 
context (that named by all but terminal
      * atomic component of the name) must already exist.
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 0abb3ec97b..27bb378fbd 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -108,6 +108,14 @@
   issues do not "pop up" wrt. others).
 -->
 <section name="Tomcat 9.0.120 (remm)" rtext="in development">
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        Avoid a race condition with concurrent lookups for a singleton JNDI
+        resource. (markt)
+      </fix>
+    </changelog>
+  </subsection>
 </section>
 <section name="Tomcat 9.0.119 (remm)" rtext="release in progress">
   <subsection name="Catalina">


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

Reply via email to