You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/08/06 10:32:07 UTC
(maven) 01/01: [MNG-8765] Pre-interpolate plugin configuration before type conversion
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch fix-maven-4-property-interpolation-ordering-for-ur
in repository https://gitbox.apache.org/repos/asf/maven.git
commit 5fd4f756f99b896eb5ae23d8bab73205d1a3efce
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Thu Aug 6 12:31:52 2026 +0200
[MNG-8765] Pre-interpolate plugin configuration before type conversion
Properties set dynamically at runtime (e.g., by Groovy/GMaven scripts
via project.properties.setProperty()) are not available during model
interpolation, which runs before the build lifecycle. When such a
property is used in a URI-typed plugin parameter like
https://example.com/${dynamic.prop}/path, the unresolved ${...} reaches
the UriConverter, which fails with URISyntaxException because curly
braces are illegal URI characters.
Fix by pre-interpolating the PlexusConfiguration tree using the
expression evaluator in DefaultMavenPluginManager before passing it to
the ComponentConfigurator. This ensures all resolvable ${...} references
are replaced before type converters process the values.
Includes integration tests for URI property interpolation with POM
properties, inherited parent properties, and CLI-passed properties.
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
.../plugin/internal/DefaultMavenPluginManager.java | 66 ++++++++++++++
...MavenITmng8765UriPropertyInterpolationTest.java | 101 +++++++++++++++++++++
.../child/pom.xml | 36 ++++++++
.../cli-property/pom.xml | 58 ++++++++++++
.../mng-8765-uri-property-interpolation/pom.xml | 70 ++++++++++++++
5 files changed, 331 insertions(+)
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
index 15f3d8df7b..3f8121a597 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
@@ -772,6 +772,14 @@ private <T> T loadV3Mojo(
validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
}
+ // MNG-8765: Pre-interpolate configuration values using the expression evaluator before
+ // the ComponentConfigurator processes them. This ensures that ${...} property references
+ // are fully resolved before type converters (like UriConverter) attempt to parse the values.
+ // Without this, properties that are not available during model interpolation (e.g., set
+ // dynamically at runtime by scripts) would reach type converters unresolved, causing
+ // failures like URISyntaxException for URI-typed parameters containing ${...}.
+ pomConfiguration = interpolateConfiguration(pomConfiguration, expressionEvaluator);
+
populateMojoExecutionFields(
mojo,
mojoExecution.getExecutionId(),
@@ -872,6 +880,64 @@ private void populateMojoExecutionFields(
}
}
+ /**
+ * Pre-interpolates a PlexusConfiguration tree by resolving all ${...} property references
+ * in configuration values using the given expression evaluator. This ensures that type
+ * converters (such as UriConverter for java.net.URI parameters) receive fully-resolved
+ * strings instead of raw expressions containing characters like curly braces that are
+ * illegal in certain types.
+ *
+ * <p>This is a defensive measure for properties that are not resolved during model
+ * interpolation — for example, properties set dynamically at runtime by scripts
+ * (e.g., Groovy/GMaven plugins that call {@code project.properties.setProperty(...)}).
+ * Model interpolation runs before the build lifecycle, so it cannot see such properties.
+ * The expression evaluator, which runs at mojo execution time, CAN see them.</p>
+ *
+ * @param configuration the plugin configuration to interpolate
+ * @param evaluator the expression evaluator to resolve ${...} references
+ * @return a new PlexusConfiguration with all resolvable expressions replaced
+ */
+ private PlexusConfiguration interpolateConfiguration(
+ PlexusConfiguration configuration, ExpressionEvaluator evaluator) {
+ // Interpolate the value of this node
+ String value = configuration.getValue(null);
+ if (value != null && value.contains("${")) {
+ try {
+ Object evaluated = evaluator.evaluate(value);
+ if (evaluated instanceof String evaluatedStr) {
+ if (!evaluatedStr.equals(value)) {
+ configuration.setValue(evaluatedStr);
+ }
+ }
+ } catch (ExpressionEvaluationException e) {
+ // Leave value as-is if evaluation fails; the ComponentConfigurator
+ // will report any conversion errors downstream
+ }
+ }
+
+ // Interpolate the default-value attribute if present
+ String defaultValue = configuration.getAttribute("default-value", null);
+ if (defaultValue != null && defaultValue.contains("${")) {
+ try {
+ Object evaluated = evaluator.evaluate(defaultValue);
+ if (evaluated instanceof String evaluatedStr) {
+ if (!evaluatedStr.equals(defaultValue)) {
+ configuration.setAttribute("default-value", evaluatedStr);
+ }
+ }
+ } catch (ExpressionEvaluationException e) {
+ // Leave default-value as-is if evaluation fails
+ }
+ }
+
+ // Recurse into children
+ for (PlexusConfiguration child : configuration.getChildren()) {
+ interpolateConfiguration(child, evaluator);
+ }
+
+ return configuration;
+ }
+
private void validateParameters(
MojoDescriptor mojoDescriptor, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator)
throws ComponentConfigurationException, PluginParameterException {
diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8765UriPropertyInterpolationTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8765UriPropertyInterpolationTest.java
new file mode 100644
index 0000000000..1fd7b0651f
--- /dev/null
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8765UriPropertyInterpolationTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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 java.util.Properties;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * This is a test set for
+ * <a href="https://issues.apache.org/jira/browse/MNG-8765">MNG-8765</a>.
+ *
+ * <p>Verifies that property interpolation runs before type conversion for
+ * URI-typed plugin parameters. When a URI parameter contains a property
+ * reference like {@code https://example.com/${my.version}/path}, the property
+ * must be resolved before the string is converted to {@link java.net.URI}.
+ * Otherwise, the curly braces cause a {@link java.net.URISyntaxException}.</p>
+ *
+ * <p>The regression was found in CloudStack (gnodet/maven4-testing#34733) where
+ * a URI parameter with {@code ${cs.version}} defined by a Groovy script at
+ * runtime was not interpolated before URI conversion.</p>
+ */
+public class MavenITmng8765UriPropertyInterpolationTest extends AbstractMavenIntegrationTestCase {
+
+ /**
+ * Verify that property interpolation resolves ${...} in URI-typed plugin
+ * parameters before type conversion, including when properties are
+ * inherited from a parent POM.
+ *
+ * @throws Exception in case of failure
+ */
+ @Test
+ public void testitPomProperty() throws Exception {
+ Path testDir = extractResources("mng-8765-uri-property-interpolation");
+
+ Verifier verifier = newVerifier(testDir);
+ verifier.setAutoclean(false);
+ verifier.deleteDirectory("target");
+ verifier.deleteDirectory("child/target");
+ verifier.addCliArgument("validate");
+ verifier.execute();
+ verifier.verifyErrorFreeLog();
+
+ // Check parent module: property defined in same POM
+ Properties parentProps = verifier.loadProperties("target/plugin-config.properties");
+ assertEquals("https://example.com/1.2.3/path", parentProps.getProperty("uriParam"));
+ assertEquals("https://example.com/1.2.3/path", parentProps.getProperty("urlParam"));
+ assertEquals("1.2.3", parentProps.getProperty("stringParam"));
+
+ // Check child module: property inherited from parent POM
+ Properties childProps = verifier.loadProperties("child/target/plugin-config.properties");
+ assertEquals("https://example.com/1.2.3/path", childProps.getProperty("uriParam"));
+ assertEquals("https://example.com/1.2.3/path", childProps.getProperty("urlParam"));
+ assertEquals("1.2.3", childProps.getProperty("stringParam"));
+ }
+
+ /**
+ * Verify that a property passed via -D on the command line is resolved in
+ * URI-typed plugin parameters. This simulates the CloudStack scenario where
+ * a Groovy script sets a property at runtime via
+ * {@code project.properties.setProperty(...)}.
+ *
+ * @throws Exception in case of failure
+ */
+ @Test
+ public void testitCliProperty() throws Exception {
+ Path testDir = extractResources("mng-8765-uri-property-interpolation/cli-property");
+
+ Verifier verifier = newVerifier(testDir);
+ verifier.setAutoclean(false);
+ verifier.deleteDirectory("target");
+ verifier.addCliArgument("-Dcli.version=2.0.0");
+ verifier.addCliArgument("validate");
+ verifier.execute();
+ verifier.verifyErrorFreeLog();
+
+ Properties props = verifier.loadProperties("target/plugin-config.properties");
+ assertEquals("https://example.com/2.0.0/path", props.getProperty("uriParam"));
+ assertEquals("https://example.com/2.0.0/path", props.getProperty("urlParam"));
+ assertEquals("2.0.0", props.getProperty("stringParam"));
+ }
+}
diff --git a/its/core-it-suite/src/test/resources/mng-8765-uri-property-interpolation/child/pom.xml b/its/core-it-suite/src/test/resources/mng-8765-uri-property-interpolation/child/pom.xml
new file mode 100644
index 0000000000..8fbf7bc6f9
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8765-uri-property-interpolation/child/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.maven.its.mng8765</groupId>
+ <artifactId>parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>child</artifactId>
+
+ <name>Maven Integration Test :: MNG-8765 :: Child</name>
+ <description>
+ Child module that inherits the test.version property from the parent
+ and uses it in a URI-typed plugin parameter.
+ </description>
+</project>
diff --git a/its/core-it-suite/src/test/resources/mng-8765-uri-property-interpolation/cli-property/pom.xml b/its/core-it-suite/src/test/resources/mng-8765-uri-property-interpolation/cli-property/pom.xml
new file mode 100644
index 0000000000..ed60c0a8e3
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8765-uri-property-interpolation/cli-property/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.maven.its.mng8765</groupId>
+ <artifactId>cli-property-test</artifactId>
+ <version>1.0-SNAPSHOT</version>
+
+ <name>Maven Integration Test :: MNG-8765 :: CLI Property</name>
+ <description>
+ Verify that a property passed via -D on the CLI is resolved in a URI-typed
+ plugin parameter. The property is NOT defined in POM properties, only via
+ -Dcli.version=2.0.0 on the command line, simulating how some plugins
+ (like Groovy scripts) dynamically set properties at runtime.
+ </description>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.its.plugins</groupId>
+ <artifactId>maven-it-plugin-configuration</artifactId>
+ <version>2.1-SNAPSHOT</version>
+ <configuration>
+ <propertiesFile>target/plugin-config.properties</propertiesFile>
+ <uriParam>https://example.com/${cli.version}/path</uriParam>
+ <urlParam>https://example.com/${cli.version}/path</urlParam>
+ <stringParam>${cli.version}</stringParam>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>config</goal>
+ </goals>
+ <phase>validate</phase>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/its/core-it-suite/src/test/resources/mng-8765-uri-property-interpolation/pom.xml b/its/core-it-suite/src/test/resources/mng-8765-uri-property-interpolation/pom.xml
new file mode 100644
index 0000000000..a2e720d57f
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8765-uri-property-interpolation/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.maven.its.mng8765</groupId>
+ <artifactId>parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <name>Maven Integration Test :: MNG-8765</name>
+ <description>
+ Verify that property interpolation runs before type conversion for URI-typed
+ plugin parameters. When a URI parameter contains a property reference like
+ https://example.com/${my.version}/path, the ${my.version} must be resolved
+ before the string is converted to java.net.URI. Otherwise, the curly braces
+ cause a URISyntaxException.
+ </description>
+
+ <properties>
+ <test.version>1.2.3</test.version>
+ </properties>
+
+ <modules>
+ <module>child</module>
+ </modules>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.its.plugins</groupId>
+ <artifactId>maven-it-plugin-configuration</artifactId>
+ <version>2.1-SNAPSHOT</version>
+ <configuration>
+ <propertiesFile>target/plugin-config.properties</propertiesFile>
+ <!-- Property from POM <properties> -->
+ <uriParam>https://example.com/${test.version}/path</uriParam>
+ <urlParam>https://example.com/${test.version}/path</urlParam>
+ <!-- Property from command line -D (not in POM properties) -->
+ <stringParam>${test.version}</stringParam>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>config</goal>
+ </goals>
+ <phase>validate</phase>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>