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

mridulpathak pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new a3ffd5bc92 Publish the entity-group map only after it is fully loaded
a3ffd5bc92 is described below

commit a3ffd5bc92d1ba0471b08bd2fba201416ec2ee92
Author: Mridul Pathak <[email protected]>
AuthorDate: Sun Nov 1 13:27:25 2026 +0530

    Publish the entity-group map only after it is fully loaded
    
    ModelGroupReader.getGroupCache assigned an empty map to the volatile 
groupCache field before filling it, so a concurrent lookup could read a 
half-built map, find no group for an entity and fall back to the delegator's 
default group, that is, the wrong datasource. The map and the group-name set 
are now built in local variables and published once complete. When nothing 
could be loaded, getGroupNames now returns the default group instead of the 
partial set a failed load used to leave behind.
---
 .../apache/ofbiz/entity/model/ModelGroupReader.java | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git 
a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelGroupReader.java
 
b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelGroupReader.java
index 9da7616854..9d4d1aa8fc 100644
--- 
a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelGroupReader.java
+++ 
b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelGroupReader.java
@@ -108,9 +108,9 @@ public class ModelGroupReader implements Serializable {
             synchronized (ModelGroupReader.class) {
                 // must check if null again as one of the blocked threads can 
still enter
                 if (this.groupCache == null) {
-                    // now it's safe
-                    this.groupCache = new HashMap<>();
-                    this.groupNames = new TreeSet<>();
+                    // build into locals and publish only once complete, so 
readers never see a partial map
+                    Map<String, String> newGroupCache = new HashMap<>();
+                    Set<String> newGroupNames = new TreeSet<>();
 
                     UtilTimer utilTimer = new UtilTimer();
                     // utilTimer.timerString("[ModelGroupReader.getGroupCache] 
Before getDocument");
@@ -125,7 +125,6 @@ public class ModelGroupReader implements Serializable {
                             Debug.logError(e, "Error loading entity group 
model", MODULE);
                         }
                         if (document == null) {
-                            this.groupCache = null;
                             return null;
                         }
 
@@ -153,8 +152,8 @@ public class ModelGroupReader implements Serializable {
                                     } catch (GenericEntityConfException e) {
                                         Debug.logWarning(e, "Exception thrown 
while getting group name: ", MODULE);
                                     }
-                                    this.groupNames.add(groupName);
-                                    this.groupCache.put(entityName, groupName);
+                                    newGroupNames.add(groupName);
+                                    newGroupCache.put(entityName, groupName);
                                     // utilTimer.timerString("  After 
entityEntityName -- " + i + " --");
                                     i++;
                                 }
@@ -165,6 +164,9 @@ public class ModelGroupReader implements Serializable {
                         }
                     }
                     utilTimer.timerString("[ModelGroupReader.getGroupCache] 
FINISHED - Total Entity-Groups: " + i + " FINISHED");
+                    // groupNames first: the volatile write of groupCache 
makes both visible to readers that see the map
+                    this.groupNames = newGroupNames;
+                    this.groupCache = newGroupCache;
                 }
             }
         }
@@ -205,15 +207,16 @@ public class ModelGroupReader implements Serializable {
         if (delegatorBaseName.indexOf('#') >= 0) {
             delegatorBaseName = delegatorBaseName.substring(0, 
delegatorBaseName.indexOf('#'));
         }
-        getGroupCache(delegatorBaseName);
-        if (this.groupNames == null) return null;
+        Set<String> loadedGroupNames = getGroupCache(delegatorBaseName) != 
null ? this.groupNames : null;
         Set<String> newSet = new HashSet<>();
         try {
             
newSet.add(EntityConfig.getInstance().getDelegator(delegatorBaseName).getDefaultGroupName());
         } catch (GenericEntityConfException e) {
             Debug.logWarning(e, "Exception thrown while getting delegator 
config: ", MODULE);
         }
-        newSet.addAll(this.groupNames);
+        if (loadedGroupNames != null) {
+            newSet.addAll(loadedGroupNames);
+        }
         return newSet;
     }
 

Reply via email to