This is an automated email from the ASF dual-hosted git repository.

slachiewicz pushed a commit to branch copilot/mng-11011-settings-interpolation
in repository https://gitbox.apache.org/repos/asf/maven.git

commit d625323ca1b96101589e3a6a11a1397003a03800
Author: Sylwester Lachiewicz <[email protected]>
AuthorDate: Sat Mar 7 20:13:00 2026 +0000

    MNG-11011 Fix proxy settings interpolation
---
 .../settings/building/DefaultSettingsBuilder.java  |  4 +-
 .../validation/DefaultSettingsValidator.java       | 11 +++++
 .../DefaultSettingsBuilderFactoryTest.java         | 44 ++++++++++++++++++
 .../validation/DefaultSettingsValidatorTest.java   | 16 +++++++
 maven-settings/src/main/mdo/settings.mdo           | 52 +++++++++++++++++-----
 5 files changed, 113 insertions(+), 14 deletions(-)

diff --git 
a/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java
 
b/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java
index 3addf78ea5..4f1db62326 100644
--- 
a/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java
+++ 
b/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java
@@ -100,8 +100,6 @@ public SettingsBuildingResult build(SettingsBuildingRequest 
request) throws Sett
 
         problems.setSource("");
 
-        userSettings = interpolate(userSettings, request, problems);
-
         // for the special case of a drive-relative Windows path, make sure 
it's absolute to save plugins from trouble
         String localRepository = userSettings.getLocalRepository();
         if (localRepository != null && localRepository.length() > 0) {
@@ -180,6 +178,8 @@ private Settings readSettings(
             return new Settings();
         }
 
+        settings = interpolate(settings, request, problems);
+
         settingsValidator.validate(settings, problems);
 
         return settings;
diff --git 
a/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java
 
b/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java
index 3706775f73..3baec828d2 100644
--- 
a/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java
+++ 
b/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java
@@ -159,6 +159,17 @@ public void validate(Settings settings, 
SettingsProblemCollector problems) {
                             "must be unique but found duplicate proxy with id 
" + proxy.getId());
                 }
                 validateStringNotEmpty(problems, "proxies.proxy.host", 
proxy.getHost(), proxy.getId());
+
+                try {
+                    Integer.parseInt(proxy.getPortString());
+                } catch (NumberFormatException e) {
+                    addViolation(
+                            problems,
+                            Severity.ERROR,
+                            "proxies.proxy[" + proxy.getId() + "].port",
+                            null,
+                            "must be a valid integer but found '" + 
proxy.getPortString() + "'");
+                }
             }
         }
     }
diff --git 
a/maven-settings-builder/src/test/java/org/apache/maven/settings/building/DefaultSettingsBuilderFactoryTest.java
 
b/maven-settings-builder/src/test/java/org/apache/maven/settings/building/DefaultSettingsBuilderFactoryTest.java
index 7aadfcb070..2aab804232 100644
--- 
a/maven-settings-builder/src/test/java/org/apache/maven/settings/building/DefaultSettingsBuilderFactoryTest.java
+++ 
b/maven-settings-builder/src/test/java/org/apache/maven/settings/building/DefaultSettingsBuilderFactoryTest.java
@@ -19,10 +19,16 @@
 package org.apache.maven.settings.building;
 
 import java.io.File;
+import java.util.Properties;
 
+import org.apache.maven.settings.Proxy;
+import org.apache.maven.settings.Settings;
 import org.junit.jupiter.api.Test;
 
+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.assertTrue;
 
 /**
  * @author Benjamin Bentmann
@@ -46,4 +52,42 @@ public void testCompleteWiring() throws Exception {
         assertNotNull(result);
         assertNotNull(result.getEffectiveSettings());
     }
+
+    @Test
+    public void testInterpolatesProxyTypedFields() throws Exception {
+        SettingsBuilder builder = new 
DefaultSettingsBuilderFactory().newInstance();
+
+        Properties systemProperties = new Properties();
+        systemProperties.setProperty("proxy.host", "proxy.example.com");
+        systemProperties.setProperty("proxy.port", "18080");
+        systemProperties.setProperty("proxy.active", "false");
+
+        DefaultSettingsBuildingRequest request = new 
DefaultSettingsBuildingRequest();
+        request.setSystemProperties(systemProperties);
+        request.setUserSettingsSource(new StringSettingsSource(
+                "<settings>"
+                        + "<proxies>"
+                        + "<proxy>"
+                        + "<id>test-proxy</id>"
+                        + "<active>${proxy.active}</active>"
+                        + "<protocol>https</protocol>"
+                        + "<host>${proxy.host}</host>"
+                        + "<port>${proxy.port}</port>"
+                        + "</proxy>"
+                        + "</proxies>"
+                        + "</settings>",
+                "test"));
+
+        SettingsBuildingResult result = builder.build(request);
+        Settings settings = result.getEffectiveSettings();
+
+        assertTrue(result.getProblems().isEmpty(), () -> "Unexpected problems: 
" + result.getProblems());
+        assertNotNull(settings);
+        assertEquals(1, settings.getProxies().size());
+
+        Proxy proxy = settings.getProxies().get(0);
+        assertEquals("proxy.example.com", proxy.getHost());
+        assertEquals(18080, proxy.getPort());
+        assertFalse(proxy.isActive());
+    }
 }
diff --git 
a/maven-settings-builder/src/test/java/org/apache/maven/settings/validation/DefaultSettingsValidatorTest.java
 
b/maven-settings-builder/src/test/java/org/apache/maven/settings/validation/DefaultSettingsValidatorTest.java
index ea91fe7388..2bac53950a 100644
--- 
a/maven-settings-builder/src/test/java/org/apache/maven/settings/validation/DefaultSettingsValidatorTest.java
+++ 
b/maven-settings-builder/src/test/java/org/apache/maven/settings/validation/DefaultSettingsValidatorTest.java
@@ -214,6 +214,22 @@ public void testValidateProxy() throws Exception {
         assertContains(problems.messages.get(0), "'proxies.proxy.host' for 
default is missing");
     }
 
+    @Test
+    public void testValidateProxyPortIsInteger() throws Exception {
+        Settings settings = new Settings();
+        Proxy proxy = new Proxy();
+        proxy.setHost("www.example.com");
+        proxy.setPortString("not-a-number");
+        settings.addProxy(proxy);
+
+        SimpleProblemCollector problems = new SimpleProblemCollector();
+        validator.validate(settings, problems);
+        assertEquals(1, problems.messages.size());
+        assertContains(
+                problems.messages.get(0),
+                "'proxies.proxy[default].port' must be a valid integer but 
found 'not-a-number'");
+    }
+
     private static class SimpleProblemCollector implements 
SettingsProblemCollector {
 
         private final List<String> messages = new ArrayList<>();
diff --git a/maven-settings/src/main/mdo/settings.mdo 
b/maven-settings/src/main/mdo/settings.mdo
index ebc6ce8127..67205087be 100644
--- a/maven-settings/src/main/mdo/settings.mdo
+++ b/maven-settings/src/main/mdo/settings.mdo
@@ -426,17 +426,17 @@
         The <code>&lt;proxy&gt;</code> element contains informations required 
to a proxy settings.
         ]]></description>
       <fields>
-        <field>
-          <name>active</name>
+        <field xml.tagName="active">
+          <name>activeString</name>
           <version>1.0.0+</version>
           <required>false</required>
           <defaultValue>true</defaultValue>
           <description>
-            <![CDATA[
-            Whether this proxy configuration is the active one.
-            ]]>
+            Whether this proxy configuration is the active one. Note: While 
the type of this field
+            is {@code String} for technical reasons, the semantic type is 
actually {@code boolean}.
+            @see #isActive()
           </description>
-          <type>boolean</type>
+          <type>String</type>
         </field>
         <field>
           <name>protocol</name>
@@ -469,15 +469,15 @@
           </description>
           <type>String</type>
         </field>
-        <field>
-          <name>port</name>
+        <field xml.tagName="port">
+          <name>portString</name>
           <version>1.0.0+</version>
           <description>
-            <![CDATA[
-            The proxy port.
-            ]]>
+            The proxy port. Note: While the type of this field is {@code 
String} for technical
+            reasons, the semantic type is actually {@code int}.
+            @see #getPort()
           </description>
-          <type>int</type>
+          <type>String</type>
           <defaultValue>8080</defaultValue>
         </field>
         <field>
@@ -502,6 +502,34 @@
           <type>String</type>
         </field>
       </fields>
+      <codeSegments>
+        <codeSegment>
+          <version>1.0.0+</version>
+          <code>
+            <![CDATA[
+    public boolean isActive()
+    {
+        return ( getActiveString() != null ) ? Boolean.parseBoolean( 
getActiveString() ) : true;
+    }
+
+    public void setActive( boolean active )
+    {
+        setActiveString( String.valueOf( active ) );
+    }
+
+    public int getPort()
+    {
+        return ( getPortString() != null ) ? Integer.parseInt( getPortString() 
) : 8080;
+    }
+
+    public void setPort( int port )
+    {
+        setPortString( String.valueOf( port ) );
+    }
+            ]]>
+          </code>
+        </codeSegment>
+      </codeSegments>
     </class>
     <class>
       <name>Server</name>

Reply via email to