gnodet-bot commented on code in PR #13197:
URL: https://github.com/apache/maven/pull/13197#discussion_r4053177903


##########
impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/AbstractUpgradeStrategy.java:
##########
@@ -94,10 +143,86 @@ public final UpgradeResult apply(UpgradeContext context, 
Map<Path, Document> pom
             context.failure("Strategy execution failed: " + e.getMessage());
             return UpgradeResult.failure(pomMap.keySet(), Set.of());
         } finally {
+            effectiveModelCache = null;
+            sharedModelBuilderSession = null;
             context.unindent();
         }
     }
 
+    /**
+     * Pre-builds effective models for every POM in the reactor with a single
+     * {@code BUILD_PROJECT} pass and stores them in {@link 
#effectiveModelCache}.
+     *
+     * <p>{@code BUILD_PROJECT} is used because it:
+     * <ul>
+     *   <li>calls {@code loadFromRoot}, which populates {@code mappedSources} 
for the
+     *       whole reactor before any single effective model is assembled — 
this is what
+     *       makes Maven 4 coordinate inference work across modules;</li>
+     *   <li>applies full profile activation (file, property, condition) 
rather than
+     *       the reduced set used by {@code BUILD_CONSUMER};</li>
+     *   <li>produces child results (via {@code getChildren()}) that map 
1-to-1 to the
+     *       reactor modules, so we can populate the cache in a single 
pass.</li>
+     * </ul>
+     *
+     * <p>The root POM is identified as the shallowest (fewest name parts) 
path in
+     * {@code pomPaths}.</p>
+     *
+     * <p>If the build fails (e.g. the project has no network access and an 
external
+     * parent cannot be resolved), the cache is left empty and
+     * {@link #buildEffectiveModel} falls back to individual {@code 
BUILD_EFFECTIVE}
+     * calls.</p>
+     *
+     * @param context  the upgrade context (used for debug/warning logging)
+     * @param pomPaths the set of POM paths that make up the reactor
+     */
+    protected void prebuildReactorModels(UpgradeContext context, Set<Path> 
pomPaths) {

Review Comment:
   **`protected` with no subclass override — should be `private`.**
   
   No subclass overrides this method. Keeping it `protected` widens the 
contract unnecessarily: any future subclass could accidentally shadow it, 
bypassing the cache setup and causing `effectiveModelCache` to remain `null` 
when `buildEffectiveModel` is called (cache lookup is skipped when `cache == 
null` — silent fallback to per-POM BUILD_EFFECTIVE, defeating the fix).
   
   ```suggestion
       private void prebuildReactorModels(UpgradeContext context, Set<Path> 
pomPaths) {
   ```



##########
impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/AbstractUpgradeStrategy.java:
##########
@@ -94,10 +143,86 @@ public final UpgradeResult apply(UpgradeContext context, 
Map<Path, Document> pom
             context.failure("Strategy execution failed: " + e.getMessage());
             return UpgradeResult.failure(pomMap.keySet(), Set.of());
         } finally {
+            effectiveModelCache = null;
+            sharedModelBuilderSession = null;
             context.unindent();
         }
     }
 
+    /**
+     * Pre-builds effective models for every POM in the reactor with a single
+     * {@code BUILD_PROJECT} pass and stores them in {@link 
#effectiveModelCache}.
+     *
+     * <p>{@code BUILD_PROJECT} is used because it:
+     * <ul>
+     *   <li>calls {@code loadFromRoot}, which populates {@code mappedSources} 
for the
+     *       whole reactor before any single effective model is assembled — 
this is what
+     *       makes Maven 4 coordinate inference work across modules;</li>
+     *   <li>applies full profile activation (file, property, condition) 
rather than
+     *       the reduced set used by {@code BUILD_CONSUMER};</li>
+     *   <li>produces child results (via {@code getChildren()}) that map 
1-to-1 to the
+     *       reactor modules, so we can populate the cache in a single 
pass.</li>
+     * </ul>
+     *
+     * <p>The root POM is identified as the shallowest (fewest name parts) 
path in
+     * {@code pomPaths}.</p>
+     *
+     * <p>If the build fails (e.g. the project has no network access and an 
external
+     * parent cannot be resolved), the cache is left empty and
+     * {@link #buildEffectiveModel} falls back to individual {@code 
BUILD_EFFECTIVE}
+     * calls.</p>
+     *
+     * @param context  the upgrade context (used for debug/warning logging)
+     * @param pomPaths the set of POM paths that make up the reactor
+     */
+    protected void prebuildReactorModels(UpgradeContext context, Set<Path> 
pomPaths) {
+        if (pomPaths.isEmpty()) {
+            return;
+        }
+        Session s = getSession();
+        ModelBuilder modelBuilder = s.getService(ModelBuilder.class);
+        sharedModelBuilderSession = modelBuilder.newSession();
+
+        // Use the shallowest path as the root (fewest name elements).
+        Path rootPom = pomPaths.stream()
+                .min(java.util.Comparator.comparingInt(Path::getNameCount))
+                .orElseThrow();
+
+        context.debug("Pre-building reactor effective models from root: " + 
rootPom);
+
+        try {
+            ModelBuilderRequest request = ModelBuilderRequest.builder()
+                    .session(s)
+                    .source(Sources.buildSource(rootPom))
+                    .requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
+                    .build();
+
+            ModelBuilderResult result = 
sharedModelBuilderSession.build(request);
+
+            // Walk the result tree and populate the cache.
+            Map<Path, Model> cache = new HashMap<>();
+            allResults(result).forEach(r -> {
+                Path path = r.getSource().getPath();
+                if (path != null) {
+                    cache.put(path.toAbsolutePath().normalize(), 
r.getEffectiveModel());
+                }
+            });
+            effectiveModelCache = cache;
+            context.debug("Reactor pre-build complete: cached " + cache.size() 
+ " effective model(s)");
+
+        } catch (Exception e) {
+            context.warning("Reactor pre-build failed, falling back to per-POM 
resolution: " + e.getMessage());

Review Comment:
   **Stack trace is swallowed — diagnostics will be poor when the pre-build 
fails.**
   
   `e.getMessage()` loses the full stack trace. In offline environments or 
projects with unresolvable external parents, this fires frequently. Add 
debug-level logging of the full exception to make failures diagnosable without 
polluting normal output:
   
   ```suggestion
               context.warning("Reactor pre-build failed, falling back to 
per-POM resolution: " + e.getMessage());
               context.debug("Reactor pre-build exception detail: " + e);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to