diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java index 26f0b4fe98f1..e1f635593f44 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java @@ -277,6 +277,10 @@ protected class ModelBuilderSessionState implements ModelProblemCollector { List externalRepositories; List repositories; + // Cycle detection chain shared across all derived sessions + // Contains both GAV coordinates (groupId:artifactId:version) and file paths + final Set parentChain; + ModelBuilderSessionState(ModelBuilderRequest request) { this( request.getSession(), @@ -286,7 +290,8 @@ protected class ModelBuilderSessionState implements ModelProblemCollector { new ConcurrentHashMap<>(64), List.of(), repos(request), - repos(request)); + repos(request), + new LinkedHashSet<>()); } static List repos(ModelBuilderRequest request) { @@ -305,7 +310,8 @@ private ModelBuilderSessionState( Map> mappedSources, List pomRepositories, List externalRepositories, - List repositories) { + List repositories, + Set parentChain) { this.session = session; this.request = request; this.result = result; @@ -314,6 +320,7 @@ private ModelBuilderSessionState( this.pomRepositories = pomRepositories; this.externalRepositories = externalRepositories; this.repositories = repositories; + this.parentChain = parentChain; this.result.setSource(this.request.getSource()); } @@ -336,8 +343,18 @@ ModelBuilderSessionState derive(ModelBuilderRequest request, DefaultModelBuilder if (session != request.getSession()) { throw new IllegalArgumentException("Session mismatch"); } + // Create a new parentChain for each derived session to prevent cycle detection issues + // The parentChain now contains both GAV coordinates and file paths return new ModelBuilderSessionState( - session, request, result, dag, mappedSources, pomRepositories, externalRepositories, repositories); + session, + request, + result, + dag, + mappedSources, + pomRepositories, + externalRepositories, + repositories, + new LinkedHashSet<>()); } @Override @@ -656,6 +673,13 @@ private void buildBuildPom() throws ModelBuilderException { mbs.buildEffectiveModel(new LinkedHashSet<>()); } catch (ModelBuilderException e) { // gathered with problem collector + // Propagate problems from child session to parent session + for (var problem : e.getResult() + .getProblemCollector() + .problems() + .toList()) { + getProblemCollector().reportProblem(problem); + } } catch (RuntimeException t) { exceptions.add(t); } finally { @@ -854,21 +878,48 @@ void buildEffectiveModel(Collection importIds) throws ModelBuilderExcept } } - Model readParent(Model childModel, Parent parent, DefaultProfileActivationContext profileActivationContext) { + Model readParent( + Model childModel, + Parent parent, + DefaultProfileActivationContext profileActivationContext, + Set parentChain) { Model parentModel; if (parent != null) { - parentModel = resolveParent(childModel, parent, profileActivationContext); + // Check for circular parent resolution using model IDs + String parentId = parent.getGroupId() + ":" + parent.getArtifactId() + ":" + parent.getVersion(); + if (!parentChain.add(parentId)) { + StringBuilder message = new StringBuilder("The parents form a cycle: "); + for (String id : parentChain) { + message.append(id).append(" -> "); + } + message.append(parentId); - if (!"pom".equals(parentModel.getPackaging())) { - add( - Severity.ERROR, - Version.BASE, - "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint(parentModel) - + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"", - parentModel.getLocation("packaging")); + add(Severity.FATAL, Version.BASE, message.toString()); + throw newModelBuilderException(); + } + + try { + parentModel = resolveParent(childModel, parent, profileActivationContext, parentChain); + + if (!"pom".equals(parentModel.getPackaging())) { + add( + Severity.ERROR, + Version.BASE, + "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint(parentModel) + + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"", + parentModel.getLocation("packaging")); + } + result.setParentModel(parentModel); + + // Recursively read the parent's parent + if (parentModel.getParent() != null) { + readParent(parentModel, parentModel.getParent(), profileActivationContext, parentChain); + } + } finally { + // Remove from chain when done processing this parent + parentChain.remove(parentId); } - result.setParentModel(parentModel); } else { String superModelVersion = childModel.getModelVersion(); if (superModelVersion == null || !KNOWN_MODEL_VERSIONS.contains(superModelVersion)) { @@ -884,20 +935,26 @@ Model readParent(Model childModel, Parent parent, DefaultProfileActivationContex } private Model resolveParent( - Model childModel, Parent parent, DefaultProfileActivationContext profileActivationContext) + Model childModel, + Parent parent, + DefaultProfileActivationContext profileActivationContext, + Set parentChain) throws ModelBuilderException { Model parentModel = null; if (isBuildRequest()) { - parentModel = readParentLocally(childModel, parent, profileActivationContext); + parentModel = readParentLocally(childModel, parent, profileActivationContext, parentChain); } if (parentModel == null) { - parentModel = resolveAndReadParentExternally(childModel, parent, profileActivationContext); + parentModel = resolveAndReadParentExternally(childModel, parent, profileActivationContext, parentChain); } return parentModel; } private Model readParentLocally( - Model childModel, Parent parent, DefaultProfileActivationContext profileActivationContext) + Model childModel, + Parent parent, + DefaultProfileActivationContext profileActivationContext, + Set parentChain) throws ModelBuilderException { ModelSource candidateSource; @@ -938,55 +995,76 @@ private Model readParentLocally( return null; } - ModelBuilderSessionState derived = derive(candidateSource); - Model candidateModel = derived.readAsParentModel(profileActivationContext); - addActivePomProfiles(derived.result.getActivePomProfiles()); + // Check for circular parent resolution using source locations (file paths) + // This must be done BEFORE calling derive() to prevent StackOverflowError + String sourceLocation = candidateSource.getLocation(); - String groupId = getGroupId(candidateModel); - String artifactId = candidateModel.getArtifactId(); - String version = getVersion(candidateModel); + if (!parentChain.add(sourceLocation)) { + StringBuilder message = new StringBuilder("The parents form a cycle: "); + for (String location : parentChain) { + message.append(location).append(" -> "); + } + message.append(sourceLocation); - // Ensure that relative path and GA match, if both are provided - if (parent.getGroupId() != null && (groupId == null || !groupId.equals(parent.getGroupId())) - || parent.getArtifactId() != null - && (artifactId == null || !artifactId.equals(parent.getArtifactId()))) { - mismatchRelativePathAndGA(childModel, parent, groupId, artifactId); - return null; + add(Severity.FATAL, Version.BASE, message.toString()); + throw newModelBuilderException(); } - if (version != null && parent.getVersion() != null && !version.equals(parent.getVersion())) { - try { - VersionRange parentRange = versionParser.parseVersionRange(parent.getVersion()); - if (!parentRange.contains(versionParser.parseVersion(version))) { - // version skew drop back to resolution from the repository - return null; - } + try { + ModelBuilderSessionState derived = derive(candidateSource); + Model candidateModel = derived.readAsParentModel(profileActivationContext, parentChain); + addActivePomProfiles(derived.result.getActivePomProfiles()); + + String groupId = getGroupId(candidateModel); + String artifactId = candidateModel.getArtifactId(); + String version = getVersion(candidateModel); + + // Ensure that relative path and GA match, if both are provided + if (parent.getGroupId() != null && (groupId == null || !groupId.equals(parent.getGroupId())) + || parent.getArtifactId() != null + && (artifactId == null || !artifactId.equals(parent.getArtifactId()))) { + mismatchRelativePathAndGA(childModel, parent, groupId, artifactId); + return null; + } - // Validate versions aren't inherited when using parent ranges the same way as when read externally. - String rawChildModelVersion = childModel.getVersion(); + if (version != null && parent.getVersion() != null && !version.equals(parent.getVersion())) { + try { + VersionRange parentRange = versionParser.parseVersionRange(parent.getVersion()); + if (!parentRange.contains(versionParser.parseVersion(version))) { + // version skew drop back to resolution from the repository + return null; + } - if (rawChildModelVersion == null) { - // Message below is checked for in the MNG-2199 core IT. - add(Severity.FATAL, Version.V31, "Version must be a constant", childModel.getLocation("")); + // Validate versions aren't inherited when using parent ranges the same way as when read + // externally. + String rawChildModelVersion = childModel.getVersion(); - } else { - if (rawChildVersionReferencesParent(rawChildModelVersion)) { + if (rawChildModelVersion == null) { // Message below is checked for in the MNG-2199 core IT. - add( - Severity.FATAL, - Version.V31, - "Version must be a constant", - childModel.getLocation("version")); + add(Severity.FATAL, Version.V31, "Version must be a constant", childModel.getLocation("")); + + } else { + if (rawChildVersionReferencesParent(rawChildModelVersion)) { + // Message below is checked for in the MNG-2199 core IT. + add( + Severity.FATAL, + Version.V31, + "Version must be a constant", + childModel.getLocation("version")); + } } - } - // MNG-2199: What else to check here ? - } catch (VersionParserException e) { - // invalid version range, so drop back to resolution from the repository - return null; + // MNG-2199: What else to check here ? + } catch (VersionParserException e) { + // invalid version range, so drop back to resolution from the repository + return null; + } } + return candidateModel; + } finally { + // Remove the source location from the chain when we're done processing this parent + parentChain.remove(sourceLocation); } - return candidateModel; } private void mismatchRelativePathAndGA(Model childModel, Parent parent, String groupId, String artifactId) { @@ -1021,7 +1099,10 @@ private void wrongParentRelativePath(Model childModel) { } Model resolveAndReadParentExternally( - Model childModel, Parent parent, DefaultProfileActivationContext profileActivationContext) + Model childModel, + Parent parent, + DefaultProfileActivationContext profileActivationContext, + Set parentChain) throws ModelBuilderException { ModelBuilderRequest request = this.request; setSource(childModel); @@ -1091,7 +1172,7 @@ Model resolveAndReadParentExternally( .source(modelSource) .build(); - Model parentModel = derive(lenientRequest).readAsParentModel(profileActivationContext); + Model parentModel = derive(lenientRequest).readAsParentModel(profileActivationContext, parentChain); if (!parent.getVersion().equals(version)) { String rawChildModelVersion = childModel.getVersion(); @@ -1180,8 +1261,8 @@ private Model readEffectiveModel() throws ModelBuilderException { profileActivationContext.setUserProperties(profileProps); } - Model parentModel = - readParent(activatedFileModel, activatedFileModel.getParent(), profileActivationContext); + Model parentModel = readParent( + activatedFileModel, activatedFileModel.getParent(), profileActivationContext, parentChain); // Now that we have read the parent, we can set the relative // path correctly if it was not set in the input model @@ -1205,7 +1286,7 @@ private Model readEffectiveModel() throws ModelBuilderException { // Mixins for (Mixin mixin : model.getMixins()) { - Model parent = resolveParent(model, mixin, profileActivationContext); + Model parent = resolveParent(model, mixin, profileActivationContext, parentChain); model = inheritanceAssembler.assembleModelInheritance(model, parent, request, this); } @@ -1623,9 +1704,10 @@ private Model doReadRawModel() throws ModelBuilderException { private record ParentModelWithProfiles(Model model, List activatedProfiles) {} /** - * Reads the request source's parent. + * Reads the request source's parent with cycle detection. */ - Model readAsParentModel(DefaultProfileActivationContext profileActivationContext) throws ModelBuilderException { + Model readAsParentModel(DefaultProfileActivationContext profileActivationContext, Set parentChain) + throws ModelBuilderException { Map parentsPerContext = cache(request.getSource(), PARENT, ConcurrentHashMap::new); @@ -1652,7 +1734,7 @@ Model readAsParentModel(DefaultProfileActivationContext profileActivationContext // into the parent recording context to maintain clean cache keys and avoid // over-recording during parent model processing. DefaultProfileActivationContext ctx = profileActivationContext.start(); - ParentModelWithProfiles modelWithProfiles = doReadAsParentModel(ctx); + ParentModelWithProfiles modelWithProfiles = doReadAsParentModel(ctx, parentChain); DefaultProfileActivationContext.Record record = ctx.stop(); replayRecordIntoContext(record, profileActivationContext); @@ -1662,9 +1744,10 @@ Model readAsParentModel(DefaultProfileActivationContext profileActivationContext } private ParentModelWithProfiles doReadAsParentModel( - DefaultProfileActivationContext childProfileActivationContext) throws ModelBuilderException { + DefaultProfileActivationContext childProfileActivationContext, Set parentChain) + throws ModelBuilderException { Model raw = readRawModel(); - Model parentData = readParent(raw, raw.getParent(), childProfileActivationContext); + Model parentData = readParent(raw, raw.getParent(), childProfileActivationContext, parentChain); DefaultInheritanceAssembler defaultInheritanceAssembler = new DefaultInheritanceAssembler(new DefaultInheritanceAssembler.InheritanceModelMerger() { @Override @@ -1685,7 +1768,7 @@ protected void mergeModel_Subprojects( }); Model parent = defaultInheritanceAssembler.assembleModelInheritance(raw, parentData, request, this); for (Mixin mixin : parent.getMixins()) { - Model parentModel = resolveParent(parent, mixin, childProfileActivationContext); + Model parentModel = resolveParent(parent, mixin, childProfileActivationContext, parentChain); parent = defaultInheritanceAssembler.assembleModelInheritance(parent, parentModel, request, this); } diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/ParentCycleDetectionTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/ParentCycleDetectionTest.java new file mode 100644 index 000000000000..3fa6eb88ceff --- /dev/null +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/ParentCycleDetectionTest.java @@ -0,0 +1,276 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.impl.model; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.maven.api.Session; +import org.apache.maven.api.services.ModelBuilder; +import org.apache.maven.api.services.ModelBuilderException; +import org.apache.maven.api.services.ModelBuilderRequest; +import org.apache.maven.api.services.ModelBuilderResult; +import org.apache.maven.api.services.Sources; +import org.apache.maven.impl.standalone.ApiRunner; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * Test for parent resolution cycle detection. + */ +class ParentCycleDetectionTest { + + Session session; + ModelBuilder modelBuilder; + + @BeforeEach + void setup() { + session = ApiRunner.createSession(); + modelBuilder = session.getService(ModelBuilder.class); + assertNotNull(modelBuilder); + } + + @Test + void testParentResolutionCycleDetectionWithRelativePath(@TempDir Path tempDir) throws IOException { + // Create .mvn directory to mark root + Files.createDirectories(tempDir.resolve(".mvn")); + + // Create a parent resolution cycle using relativePath: child -> parent -> child + // This reproduces the same issue as the integration test MavenITmng11009StackOverflowParentResolutionTest + Path childPom = tempDir.resolve("pom.xml"); + Files.writeString( + childPom, + """ + + 4.0.0 + + org.apache.maven.its.mng11009 + parent + 1.0-SNAPSHOT + parent + + child + pom + + """); + + Path parentPom = tempDir.resolve("parent").resolve("pom.xml"); + Files.createDirectories(parentPom.getParent()); + Files.writeString( + parentPom, + """ + + 4.0.0 + + org.apache.maven.its.mng11009 + external-parent + 1.0-SNAPSHOT + + + parent + pom + + """); + + ModelBuilderRequest request = ModelBuilderRequest.builder() + .session(session) + .source(Sources.buildSource(childPom)) + .requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT) + .build(); + + // This should either: + // 1. Detect the cycle and throw a meaningful ModelBuilderException, OR + // 2. Not cause a StackOverflowError (the main goal is to prevent the StackOverflowError) + try { + ModelBuilderResult result = modelBuilder.newSession().build(request); + // If we get here without StackOverflowError, that's actually good progress + // The build may still fail with a different error (circular dependency), but that's expected + System.out.println("Build completed without StackOverflowError. Result: " + result); + } catch (StackOverflowError error) { + fail( + "Build failed with StackOverflowError, which should be prevented. This indicates the cycle detection is not working properly for relativePath-based cycles."); + } catch (ModelBuilderException exception) { + // This is acceptable - the build should fail with a meaningful error, not StackOverflowError + System.out.println("Build failed with ModelBuilderException (expected): " + exception.getMessage()); + // Check if it's a cycle detection error + if (exception.getMessage().contains("cycle") + || exception.getMessage().contains("circular")) { + System.out.println("✓ Cycle detected correctly!"); + } + // We don't assert on the specific message because the main goal is to prevent StackOverflowError + } + } + + @Test + void testDirectCycleDetection(@TempDir Path tempDir) throws IOException { + // Create .mvn directory to mark root + Files.createDirectories(tempDir.resolve(".mvn")); + + // Create a direct cycle: A -> B -> A + Path pomA = tempDir.resolve("a").resolve("pom.xml"); + Files.createDirectories(pomA.getParent()); + Files.writeString( + pomA, + """ + + 4.0.0 + test + a + 1.0 + + test + b + 1.0 + ../b/pom.xml + + + """); + + Path pomB = tempDir.resolve("b").resolve("pom.xml"); + Files.createDirectories(pomB.getParent()); + Files.writeString( + pomB, + """ + + 4.0.0 + test + b + 1.0 + + test + a + 1.0 + ../a/pom.xml + + + """); + + ModelBuilderRequest request = ModelBuilderRequest.builder() + .session(session) + .source(Sources.buildSource(pomA)) + .requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT) + .build(); + + // This should detect the cycle and throw a meaningful ModelBuilderException + try { + ModelBuilderResult result = modelBuilder.newSession().build(request); + fail("Expected ModelBuilderException due to cycle detection, but build succeeded: " + result); + } catch (StackOverflowError error) { + fail("Build failed with StackOverflowError, which should be prevented by cycle detection."); + } catch (ModelBuilderException exception) { + // This is expected - the build should fail with a cycle detection error + System.out.println("Build failed with ModelBuilderException (expected): " + exception.getMessage()); + // Check if it's a cycle detection error + if (exception.getMessage().contains("cycle") + || exception.getMessage().contains("circular")) { + System.out.println("✓ Cycle detected correctly!"); + } else { + System.out.println("⚠ Exception was not a cycle detection error: " + exception.getMessage()); + } + } + } + + @Test + void testMultipleModulesWithSameParentDoNotCauseCycle(@TempDir Path tempDir) throws IOException { + // Create .mvn directory to mark root + Files.createDirectories(tempDir.resolve(".mvn")); + + // Create a scenario like the failing test: multiple modules with the same parent + Path parentPom = tempDir.resolve("parent").resolve("pom.xml"); + Files.createDirectories(parentPom.getParent()); + Files.writeString( + parentPom, + """ + + 4.1.0 + test + parent + 1.0 + pom + + """); + + Path moduleA = tempDir.resolve("module-a").resolve("pom.xml"); + Files.createDirectories(moduleA.getParent()); + Files.writeString( + moduleA, + """ + + 4.1.0 + + test + parent + 1.0 + ../parent/pom.xml + + module-a + + """); + + Path moduleB = tempDir.resolve("module-b").resolve("pom.xml"); + Files.createDirectories(moduleB.getParent()); + Files.writeString( + moduleB, + """ + + 4.1.0 + + test + parent + 1.0 + ../parent/pom.xml + + module-b + + """); + + // Both modules should be able to resolve their parent without cycle detection errors + ModelBuilderRequest requestA = ModelBuilderRequest.builder() + .session(session) + .source(Sources.buildSource(moduleA)) + .requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT) + .build(); + + ModelBuilderRequest requestB = ModelBuilderRequest.builder() + .session(session) + .source(Sources.buildSource(moduleB)) + .requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT) + .build(); + + // These should not throw exceptions + ModelBuilderResult resultA = modelBuilder.newSession().build(requestA); + ModelBuilderResult resultB = modelBuilder.newSession().build(requestB); + + // Verify that both models were built successfully + assertNotNull(resultA); + assertNotNull(resultB); + } +} diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng11009StackOverflowParentResolutionTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng11009StackOverflowParentResolutionTest.java new file mode 100644 index 000000000000..d79dd553b97d --- /dev/null +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng11009StackOverflowParentResolutionTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.it; + +import java.io.File; + +import org.junit.jupiter.api.Test; + +/** + * This is a test set for Issue #11009. + * + * @author Guillaume Nodet + */ +public class MavenITmng11009StackOverflowParentResolutionTest extends AbstractMavenIntegrationTestCase { + + public MavenITmng11009StackOverflowParentResolutionTest() { + super("[4.0.0-rc-3,)"); + } + + /** + * Test that circular parent resolution doesn't cause a StackOverflowError during project model building. + * This reproduces the issue where: + * - Root pom.xml has parent with relativePath="parent" + * - parent/pom.xml has parent without relativePath (defaults to "../pom.xml") + * - This creates a circular parent resolution that causes stack overflow in hashCode calculation + * + * @throws Exception in case of failure + */ + @Test + public void testStackOverflowInParentResolution() throws Exception { + File testDir = extractResources("/mng-11009-stackoverflow-parent-resolution"); + + Verifier verifier = newVerifier(testDir.getAbsolutePath()); + verifier.setAutoclean(false); + verifier.deleteArtifacts("org.apache.maven.its.mng11009"); + + // This should fail gracefully with a meaningful error message, not with StackOverflowError + try { + verifier.addCliArgument("validate"); + verifier.execute(); + // If we get here without StackOverflowError, the fix is working + // The build may still fail with a different error (circular dependency), but that's expected + } catch (Exception e) { + // Check that it's not a StackOverflowError + String errorMessage = e.getMessage(); + if (errorMessage != null && errorMessage.contains("StackOverflowError")) { + throw new AssertionError("Build failed with StackOverflowError, which should be fixed", e); + } + // Other errors are acceptable as the POM structure is intentionally problematic + } + + // The main goal is to not get a StackOverflowError + // We expect some kind of circular dependency error instead + } +} diff --git a/its/core-it-suite/src/test/resources/mng-11009-stackoverflow-parent-resolution/parent/pom.xml b/its/core-it-suite/src/test/resources/mng-11009-stackoverflow-parent-resolution/parent/pom.xml new file mode 100644 index 000000000000..12d10e51b62a --- /dev/null +++ b/its/core-it-suite/src/test/resources/mng-11009-stackoverflow-parent-resolution/parent/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + + org.apache.maven.its.mng11009 + external-parent + 1.0-SNAPSHOT + + + + parent + pom + + Maven Integration Test :: MNG-11009 :: Parent + Parent POM that creates circular reference by having a parent without relativePath. + diff --git a/its/core-it-suite/src/test/resources/mng-11009-stackoverflow-parent-resolution/pom.xml b/its/core-it-suite/src/test/resources/mng-11009-stackoverflow-parent-resolution/pom.xml new file mode 100644 index 000000000000..8226201ecbcd --- /dev/null +++ b/its/core-it-suite/src/test/resources/mng-11009-stackoverflow-parent-resolution/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + + org.apache.maven.its.mng11009 + parent + 1.0-SNAPSHOT + parent + + + child + pom + + Maven Integration Test :: MNG-11009 :: Child + Test case for StackOverflowError during project model building with circular parent resolution. +