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 ca739fb1054e12f2e797957ffb6bf6843ea202ec
Author: opencode <[email protected]>
AuthorDate: Thu Oct 15 10:55:01 2026 +0200

    Synchronize the registry metadata maps
    
    The descriptors, descriptorsByClass and searchedPaths maps were plain
    HashMaps mutated by addManagedBean() and loadDescriptors() and read
    by findManagedBean(), registerComponent() and findDescriptor() with no
    synchronization, while registerComponent() is invoked concurrently
    during parallel web application deployment and from worker threads on
    first use of an upgrade protocol. Concurrent access could lose
    updates or transiently corrupt the map state, and stop() additionally
    reassigned the three non-volatile fields while readers were active.
    
    Make the three maps final ConcurrentHashMaps and have stop() clear
    them rather than reassign them. Record the searched package
    atomically with putIfAbsent so concurrent first loads of the same
    package can not both parse the descriptor file, and skip the
    name-keyed entry when a ManagedBean has no name (permitted by the
    DTD) since ConcurrentHashMap does not allow null keys, preserving the
    previous behavior for such descriptors.
---
 java/org/apache/tomcat/util/modeler/Registry.java | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/java/org/apache/tomcat/util/modeler/Registry.java 
b/java/org/apache/tomcat/util/modeler/Registry.java
index 47d5746c1b..a369b1eadf 100644
--- a/java/org/apache/tomcat/util/modeler/Registry.java
+++ b/java/org/apache/tomcat/util/modeler/Registry.java
@@ -21,10 +21,10 @@ import java.io.FileInputStream;
 import java.io.InputStream;
 import java.lang.management.ManagementFactory;
 import java.net.URL;
-import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.management.DynamicMBean;
 import javax.management.InstanceNotFoundException;
@@ -72,15 +72,15 @@ public class Registry implements RegistryMBean, 
MBeanRegistration {
     /**
      * The set of ManagedBean instances for the beans this registry knows 
about, keyed by name.
      */
-    private Map<String,ManagedBean> descriptors = new HashMap<>();
+    private final Map<String,ManagedBean> descriptors = new 
ConcurrentHashMap<>();
 
     /**
      * List of managed beans, keyed by class name
      */
-    private Map<String,ManagedBean> descriptorsByClass = new HashMap<>();
+    private final Map<String,ManagedBean> descriptorsByClass = new 
ConcurrentHashMap<>();
 
     // map to avoid duplicated searching or loading descriptors
-    private Map<String,URL> searchedPaths = new HashMap<>();
+    private final Map<String,URL> searchedPaths = new ConcurrentHashMap<>();
 
     private Object guard;
 
@@ -168,9 +168,9 @@ public class Registry implements RegistryMBean, 
MBeanRegistration {
      */
     @Override
     public void stop() {
-        descriptorsByClass = new HashMap<>();
-        descriptors = new HashMap<>();
-        searchedPaths = new HashMap<>();
+        descriptorsByClass.clear();
+        descriptors.clear();
+        searchedPaths.clear();
     }
 
 
@@ -297,7 +297,9 @@ public class Registry implements RegistryMBean, 
MBeanRegistration {
      */
     public void addManagedBean(ManagedBean bean) {
         // XXX Use group + name
-        descriptors.put(bean.getName(), bean);
+        if (bean.getName() != null) {
+            descriptors.put(bean.getName(), bean);
+        }
         if (bean.getType() != null) {
             descriptorsByClass.put(bean.getType(), bean);
         }
@@ -682,7 +684,10 @@ public class Registry implements RegistryMBean, 
MBeanRegistration {
         if (log.isTraceEnabled()) {
             log.trace("Found " + dURL);
         }
-        searchedPaths.put(packageName, dURL);
+        if (searchedPaths.putIfAbsent(packageName, dURL) != null) {
+            // Another thread claimed this package first and is loading it
+            return;
+        }
         try {
             load("MbeansDescriptorsDigesterSource", dURL, null);
         } catch (Exception e) {


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

Reply via email to