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

gnodet pushed a commit to branch maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/maven-4.0.x by this push:
     new 4755cfcbd3 Fix race condition in concurrent builder: reactor artifact 
file snapshotted too early (#13728) (#13734)
4755cfcbd3 is described below

commit 4755cfcbd33761441da9c6008eb4b6e1dc705396
Author: Guillaume Nodet <[email protected]>
AuthorDate: Wed Oct 28 18:39:05 2026 +0100

    Fix race condition in concurrent builder: reactor artifact file snapshotted 
too early (#13728) (#13734)
---
 .../internal/LifecycleDependencyResolver.java      | 20 ++++++---
 .../internal/LifecycleDependencyResolverTest.java  | 50 ++++++++++++++++++++++
 2 files changed, 63 insertions(+), 7 deletions(-)

diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
 
b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
index e25472ec92..cc4b2191bb 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
@@ -128,12 +128,15 @@ public void resolveProjectDependencies(
             Set<Artifact> resolvedArtifacts = resolveProjectArtifacts(
                     project, scopesToCollect, scopesToResolve, session, 
aggregating, projectArtifacts);
 
-            Map<Artifact, File> reactorProjects =
+            // MNG-6300: build a live-lookup map so that reactor files are 
always up-to-date.
+            // In the concurrent builder a sibling project may install its jar 
*after* this
+            // resolution starts, so we must read getFile() at lookup time 
rather than
+            // snapshotting it up-front (a null snapshot would prevent the 
file from ever
+            // being set, causing NPEs in plugins such as 
maven-dependency-plugin:properties).
+            Map<Artifact, MavenProject> reactorProjects =
                     new HashMap<>(session.getProjects().size());
             for (MavenProject reactorProject : session.getProjects()) {
-                reactorProjects.put(
-                        reactorProject.getArtifact(),
-                        reactorProject.getArtifact().getFile());
+                reactorProjects.put(reactorProject.getArtifact(), 
reactorProject);
             }
 
             Map<String, Artifact> map = new HashMap<>();
@@ -143,9 +146,12 @@ public void resolveProjectDependencies(
                  * During lifecycle the Artifact.getFile() can change from 
target/classes to the actual jar.
                  * This clearly shows that target/classes should not be abused 
as artifactFile just for the classpath
                  */
-                File reactorProjectFile = reactorProjects.get(artifact);
-                if (reactorProjectFile != null) {
-                    artifact.setFile(reactorProjectFile);
+                MavenProject reactorProject = reactorProjects.get(artifact);
+                if (reactorProject != null) {
+                    File reactorProjectFile = 
reactorProject.getArtifact().getFile();
+                    if (reactorProjectFile != null) {
+                        artifact.setFile(reactorProjectFile);
+                    }
                 }
 
                 map.put(artifact.getDependencyConflictId(), artifact);
diff --git 
a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolverTest.java
 
b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolverTest.java
index 3ff43bc2d2..f627a3b34c 100644
--- 
a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolverTest.java
+++ 
b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolverTest.java
@@ -79,4 +79,54 @@ void testCachedReactorProjectDependencies() throws Exception 
{
                         .get("org.apache.maven.its.mng6300:mng6300-lib")
                         .getFile());
     }
+
+    /**
+     * Verifies that resolving a downstream project (war) does not throw when 
a reactor sibling
+     * (lib) has a {@code null} artifact file — i.e. the sibling has not yet 
packaged its jar.
+     *
+     * <p>This guards the null-file code path introduced by the fix in
+     * <a href="https://github.com/apache/maven/pull/13728";>#13728</a>. The 
concurrent-builder
+     * race (sibling file is {@code null} at map-build time but non-null at 
lookup time within the
+     * same call) cannot be reproduced in a sequential unit test; this test 
covers the observable
+     * contract: when the sibling file is {@code null}, resolution completes 
without error and the
+     * resolved artifact's file remains {@code null} rather than causing an 
NPE downstream.
+     */
+    @Test
+    void testReactorDependencyWithNullArtifactFileDoesNotThrow() throws 
Exception {
+        MavenSession session = createMavenSession(
+                new 
File("src/test/projects/lifecycle-dependency-resolver/pom.xml"), new 
Properties(), true);
+        Collection<String> scopesToCollect = null;
+        Collection<String> scopesToResolve = 
Collections.singletonList("compile");
+        boolean aggregating = false;
+
+        MavenProject root = session.getProjects().get(0);
+        MavenProject lib = session.getProjects().get(1);
+        MavenProject war = session.getProjects().get(2);
+
+        // lib has not yet produced its jar (concurrent builder: packaging not 
yet complete)
+        lib.getArtifact().setFile(null);
+
+        Set<Artifact> reactorArtifacts = new HashSet<>();
+        root.setArtifactFilter(artifact -> true);
+        lib.setArtifactFilter(artifact -> true);
+        war.setArtifactFilter(artifact -> true);
+
+        resolver.resolveProjectDependencies(
+                root, scopesToCollect, scopesToResolve, session, aggregating, 
reactorArtifacts);
+        reactorArtifacts.add(root.getArtifact());
+
+        resolver.resolveProjectDependencies(
+                lib, scopesToCollect, scopesToResolve, session, aggregating, 
reactorArtifacts);
+        reactorArtifacts.add(lib.getArtifact());
+
+        // Resolution of war must complete without NPE even though lib's 
artifact file is null
+        resolver.resolveProjectDependencies(
+                war, scopesToCollect, scopesToResolve, session, aggregating, 
reactorArtifacts);
+
+        assertNull(
+                war.getArtifactMap()
+                        .get("org.apache.maven.its.mng6300:mng6300-lib")
+                        .getFile(),
+                "resolved lib artifact file should remain null when lib has 
not yet packaged its jar");
+    }
 }

Reply via email to