You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by sl...@apache.org on 2026/10/05 20:04:06 UTC
(maven) 01/01: Fix #13294: build a parent outside the reactor once per POM
This is an automated email from the ASF dual-hosted git repository.
slachiewicz pushed a commit to branch agent/model-reread-13294-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git
commit 982d09eefefa20e4890650c8f14152a7c14e5425
Author: Sylwester Lachiewicz <sl...@apache.org>
AuthorDate: Mon Oct 5 21:53:28 2026 +0200
Fix #13294: build a parent outside the reactor once per POM
Reactor POM sources are request-scoped in the model cache, so building
the parent project again for each child re-read its whole parent chain
from disk. Children of the same parent POM now share one parent project.
---
.../maven/project/DefaultProjectBuilder.java | 17 +++++-
.../apache/maven/project/ProjectBuilderTest.java | 66 ++++++++++++++++++++++
2 files changed, 81 insertions(+), 2 deletions(-)
diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
index 51ee3e0d01..1ec66c6a27 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
@@ -316,6 +316,11 @@ class BuildSession implements AutoCloseable {
private final ModelBuilder.ModelBuilderSession modelBuilderSession;
private final Map<String, MavenProject> projectIndex = new ConcurrentHashMap<>(256);
+ // Parents outside the reactor, built once per POM file and shared by all their children. Reactor POM
+ // sources are request-scoped in the model cache, so rebuilding a parent per child re-reads its whole
+ // parent chain from disk each time
+ private final Map<Path, MavenProject> parentProjects = new ConcurrentHashMap<>();
+
// Store computed repositories per project to avoid leakage between projects
private final Map<String, List<ArtifactRepository>> projectRepositories = new ConcurrentHashMap<>();
@@ -1079,9 +1084,17 @@ private void initParent(MavenProject project, ModelBuilderResult result) {
Path parentPomFile = parentModel.getPomFile();
if (parentPomFile != null) {
project.setParentFile(parentPomFile.toFile());
+ Path parentKey = parentPomFile.toAbsolutePath().normalize();
try {
- parent = build(true, parentPomFile, Sources.buildSource(parentPomFile))
- .getProject();
+ parent = parentProjects.get(parentKey);
+ if (parent == null) {
+ parent = build(true, parentPomFile, Sources.buildSource(parentPomFile))
+ .getProject();
+ MavenProject existing = parentProjects.putIfAbsent(parentKey, parent);
+ if (existing != null) {
+ parent = existing;
+ }
+ }
} catch (ProjectBuildingException e) {
// MNG-4488 where let invalid parents slide on by
if (logger.isDebugEnabled()) {
diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
index 0d55cbed90..8565287c55 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
@@ -57,6 +57,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -197,6 +198,71 @@ void testReadModifiedPoms(@TempDir Path tempDir) throws Exception {
assertThat(result.getProject().getProperties(), hasKey((Object) "addedProperty"));
}
+ @Test
+ void testParentOutsideReactorIsBuiltOnce(@TempDir Path tempDir) throws Exception {
+ Files.writeString(tempDir.resolve("pom.xml"), """
+ <project>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>test</groupId>
+ <artifactId>root</artifactId>
+ <version>1</version>
+ <packaging>pom</packaging>
+ <properties>
+ <inherited>root</inherited>
+ </properties>
+ </project>
+ """);
+ Path parentPom = Files.createDirectory(tempDir.resolve("parent")).resolve("pom.xml");
+ Files.writeString(parentPom, """
+ <project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>test</groupId>
+ <artifactId>root</artifactId>
+ <version>1</version>
+ </parent>
+ <artifactId>parent</artifactId>
+ <packaging>pom</packaging>
+ </project>
+ """);
+ List<File> children = new ArrayList<>();
+ for (String name : List.of("a", "b")) {
+ Path childPom = Files.createDirectories(tempDir.resolve("parent").resolve(name))
+ .resolve("pom.xml");
+ Files.writeString(childPom, """
+ <project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>test</groupId>
+ <artifactId>parent</artifactId>
+ <version>1</version>
+ </parent>
+ <artifactId>%s</artifactId>
+ </project>
+ """.formatted(name));
+ children.add(childPom.toFile());
+ }
+
+ MavenSession session = createMavenSession(null);
+ session.getRequest().setRootDirectory(tempDir);
+ ProjectBuildingRequest request = new DefaultProjectBuildingRequest();
+ request.setRepositorySession(session.getRepositorySession());
+ List<ProjectBuildingResult> results = getContainer()
+ .lookup(org.apache.maven.project.ProjectBuilder.class)
+ .build(children, false, request);
+
+ assertEquals(2, results.size());
+ MavenProject a = results.get(0).getProject();
+ MavenProject b = results.get(1).getProject();
+ assertEquals("root", a.getProperties().getProperty("inherited"));
+ assertNotNull(a.getParent());
+ assertEquals("parent", a.getParent().getArtifactId());
+ assertEquals("root", a.getParent().getParent().getArtifactId());
+ // both children share one parent chain instead of rebuilding it from disk (GH-13294)
+ assertSame(a.getParent(), b.getParent());
+ assertSame(a.getParent().getParent(), b.getParent().getParent());
+ }
+
@Test
void testReadErroneousMavenProjectContainsReference() throws Exception {
File pomFile = new File("src/test/resources/projects/artifactMissingVersion/pom.xml").getAbsoluteFile();