diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelInterpolator.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelInterpolator.java index 6194c1289f61..2e8c7b1fe84a 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelInterpolator.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelInterpolator.java @@ -179,22 +179,24 @@ String doCallback( return new MavenBuildTimestamp(request.getSession().getStartTime(), model.getProperties()) .formattedTimestamp(); } - // prefixed model reflection - for (String prefix : getProjectPrefixes(request)) { - if (expression.startsWith(prefix)) { - String subExpr = expression.substring(prefix.length()); - String v = projectProperty(model, projectDir, subExpr, true); - if (v != null) { - return v; - } - } - } // user properties String value = request.getUserProperties().get(expression); - // model properties + // model properties (check before prefixed model reflection to avoid recursion) if (value == null) { value = model.getProperties().get(expression); } + // prefixed model reflection + if (value == null) { + for (String prefix : getProjectPrefixes(request)) { + if (expression.startsWith(prefix)) { + String subExpr = expression.substring(prefix.length()); + value = projectProperty(model, projectDir, subExpr, true); + if (value != null) { + return value; + } + } + } + } // system properties if (value == null) { value = request.getSystemProperties().get(expression); diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelInterpolatorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelInterpolatorTest.java index 7afe7a82e2de..065bc79ecd5e 100644 --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelInterpolatorTest.java +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelInterpolatorTest.java @@ -575,6 +575,26 @@ void shouldIgnorePropertiesWithPomPrefix() throws Exception { assertEquals(uninterpolatedName, out.getName()); } + @Test + void testProjectUrlPropertyDoesNotCauseRecursion() throws Exception { + // GH-11384: ${project.url} should resolve to the property "project.url" before + // trying to resolve via model reflection, which would cause recursion + Map modelProperties = new HashMap<>(); + modelProperties.put("project.url", "https://github.com/slackapi/java-slack-sdk"); + + Model model = Model.newBuilder() + .url("${project.url}") + .properties(modelProperties) + .build(); + + SimpleProblemCollector collector = new SimpleProblemCollector(); + Model out = interpolator.interpolateModel( + model, null, createModelBuildingRequest(context).build(), collector); + + assertProblemFree(collector); + assertEquals("https://github.com/slackapi/java-slack-sdk", out.getUrl()); + } + @Provides @Priority(10) @SuppressWarnings("unused") diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11384RecursiveVariableReferenceTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11384RecursiveVariableReferenceTest.java new file mode 100644 index 000000000000..5c60d9082091 --- /dev/null +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11384RecursiveVariableReferenceTest.java @@ -0,0 +1,52 @@ +/* + * 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.nio.file.Path; + +import org.junit.jupiter.api.Test; + +/** + * This is a test set for GH-11384. + * + * Verifies that ${project.url} can refer to a property named "project.url" without causing + * a recursive variable reference error. This pattern is used by slack-sdk-parent. + * + * @since 4.0.0-rc-4 + */ +class MavenITgh11384RecursiveVariableReferenceTest extends AbstractMavenIntegrationTestCase { + + /** + * Verify that ${project.url} in the url field can reference a property named project.url + * without causing a recursive variable reference error. + */ + @Test + void testIt() throws Exception { + Path basedir = extractResources("/gh-11384").getAbsoluteFile().toPath(); + + Verifier verifier = newVerifier(basedir.toString()); + verifier.addCliArgument("help:effective-pom"); + verifier.execute(); + verifier.verifyErrorFreeLog(); + + // Verify that the URL was correctly interpolated from the property + verifier.verifyTextInLog("https://github.com/slackapi/java-slack-sdk"); + } +} + diff --git a/its/core-it-suite/src/test/resources/gh-11384/pom.xml b/its/core-it-suite/src/test/resources/gh-11384/pom.xml new file mode 100644 index 000000000000..0c23b6b95c84 --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-11384/pom.xml @@ -0,0 +1,18 @@ + + + org.apache.maven.its.mng11384 + test + 1.0 + pom + + Maven Integration Test :: GH-11384 + Test that project.url can refer to a property named project.url without causing recursion + + + ${project.url} + + + https://github.com/slackapi/java-slack-sdk + + +