From b74199ed446c83282ad806afaf962a7f74eac0ea Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 25 Nov 2020 20:12:04 +0100 Subject: [PATCH] [MNG-7034] StackOverflowError thrown if a cycle exists in BOM imports This closes #484 --- .../model/building/DefaultModelBuilder.java | 11 +- .../building/DefaultModelBuilderTest.java | 153 ++++++++++++++++++ 2 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 maven-model-builder/src/test/java/org/apache/maven/model/building/DefaultModelBuilderTest.java diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java index a8568efb27..a089f9d991 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java @@ -248,6 +248,13 @@ public class DefaultModelBuilder @Override public ModelBuildingResult build( ModelBuildingRequest request ) throws ModelBuildingException + { + return build( request, new LinkedHashSet() ); + } + + @SuppressWarnings( "checkstyle:methodlength" ) + protected ModelBuildingResult build( ModelBuildingRequest request, Collection importIds ) + throws ModelBuildingException { // phase 1 DefaultModelBuildingResult result = new DefaultModelBuildingResult(); @@ -427,7 +434,7 @@ public class DefaultModelBuilder if ( !request.isTwoPhaseBuilding() ) { - build( request, result ); + build( request, result, importIds ); } return result; @@ -1303,7 +1310,7 @@ public class DefaultModelBuilder final ModelBuildingResult importResult; try { - importResult = build( importRequest ); + importResult = build( importRequest, importIds ); } catch ( ModelBuildingException e ) { diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/building/DefaultModelBuilderTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/building/DefaultModelBuilderTest.java new file mode 100644 index 0000000000..4e7919a6c2 --- /dev/null +++ b/maven-model-builder/src/test/java/org/apache/maven/model/building/DefaultModelBuilderTest.java @@ -0,0 +1,153 @@ +package org.apache.maven.model.building; + + /* + * 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. + */ + +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Parent; +import org.apache.maven.model.Repository; +import org.apache.maven.model.resolution.InvalidRepositoryException; +import org.apache.maven.model.resolution.ModelResolver; +import org.apache.maven.model.resolution.UnresolvableModelException; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +/** + * @author Guillaume Nodet + */ +public class DefaultModelBuilderTest +{ + + private static final String BASE1_ID = "thegroup:base1:pom"; + private static final String BASE1_ID2 = "thegroup:base1:1"; + + private static final String BASE1 = "\n" + + " 4.0.0\n" + + " thegroup\n" + + " base1\n" + + " 1\n" + + " pom\n" + + " \n" + + " \n" + + " \n" + + " thegroup\n" + + " base2\n" + + " 1\n" + + " pom\n" + + " import\n" + + " \n" + + " \n" + + " \n" + + "\n"; + + private static final String BASE2_ID = "thegroup:base2:pom"; + private static final String BASE2_ID2 = "thegroup:base2:1"; + + private static final String BASE2 = "\n" + + " 4.0.0\n" + + " thegroup\n" + + " base2\n" + + " 1\n" + + " pom\n" + + " \n" + + " \n" + + " \n" + + " thegroup\n" + + " base1\n" + + " 1\n" + + " pom\n" + + " import\n" + + " \n" + + " \n" + + " \n" + + "\n"; + + @Test( expected = ModelBuildingException.class ) + public void testCycleInImports() + throws Exception + { + ModelBuilder builder = new DefaultModelBuilderFactory().newInstance(); + assertNotNull( builder ); + + DefaultModelBuildingRequest request = new DefaultModelBuildingRequest(); + request.setModelSource( new StringModelSource( BASE1 ) ); + request.setModelResolver( new CycleInImportsResolver() ); + + builder.build( request ); + } + + static class CycleInImportsResolver extends BaseModelResolver + { + @Override + public ModelSource resolveModel(Dependency dependency) throws UnresolvableModelException + { + switch ( dependency.getManagementKey() ) + { + case BASE1_ID: return new StringModelSource( BASE1 ); + case BASE2_ID: return new StringModelSource( BASE2 ); + } + return null; + } + } + + static class BaseModelResolver implements ModelResolver + { + @Override + public ModelSource resolveModel( String groupId, String artifactId, String version ) + throws UnresolvableModelException + { + switch ( groupId + ":" + artifactId + ":" + version ) + { + case BASE1_ID2: return new StringModelSource( BASE1 ); + case BASE2_ID2: return new StringModelSource( BASE2 ); + } + return null; + } + + @Override + public ModelSource resolveModel( Parent parent ) throws UnresolvableModelException + { + return null; + } + + @Override + public ModelSource resolveModel( Dependency dependency ) throws UnresolvableModelException + { + return null; + } + + @Override + public void addRepository( Repository repository ) throws InvalidRepositoryException + { + } + + @Override + public void addRepository(Repository repository, boolean replace) throws InvalidRepositoryException + { + } + + @Override + public ModelResolver newCopy() + { + return this; + } + } + +}