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

cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new bf6e6ee00a [MNG-8544] Conflicting extensions went unnoticed (#2063)
bf6e6ee00a is described below

commit bf6e6ee00a07ad0514b45c79fa76acb17805bf44
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Tue Jan 28 12:54:26 2025 +0100

    [MNG-8544] Conflicting extensions went unnoticed (#2063)
    
    Now that Maven4 has several extension sources, we need to be
    able to detect conflicts. For now, we allow only one GA
    and fail the bootstrapping, if conflict discovered.
    
    User should fix the situation, with the help of detailed
    error message enumerating all the conflicts.
    
    ---
    
    https://issues.apache.org/jira/browse/MNG-8544
---
 .../org/apache/maven/cling/invoker/BaseParser.java | 38 +++++++++++++++++++---
 .../maven/cling/invoker/mvn/MavenInvokerTest.java  | 33 +++++++++++++++++++
 .../cling/invoker/mvn/MavenInvokerTestSupport.java |  2 +-
 3 files changed, 67 insertions(+), 6 deletions(-)

diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
index 27f4add0af..982b1320b7 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
@@ -295,17 +295,45 @@ protected Map<String, String> 
populateUserProperties(LocalContext context) throw
 
     protected List<CoreExtension> readCoreExtensionsDescriptor(LocalContext 
context)
             throws ParserException, IOException {
-        ArrayList<CoreExtension> extensions = new ArrayList<>();
         String installationExtensionsFile = 
context.userProperties.get(Constants.MAVEN_INSTALLATION_EXTENSIONS);
-        extensions.addAll(readCoreExtensionsDescriptorFromFile(
+        ArrayList<CoreExtension> installationExtensions = new 
ArrayList<>(readCoreExtensionsDescriptorFromFile(
                 
context.installationDirectory.resolve(installationExtensionsFile)));
 
+        String userExtensionsFile = 
context.userProperties.get(Constants.MAVEN_USER_EXTENSIONS);
+        ArrayList<CoreExtension> userExtensions = new ArrayList<>(
+                
readCoreExtensionsDescriptorFromFile(context.userHomeDirectory.resolve(userExtensionsFile)));
+
         String projectExtensionsFile = 
context.userProperties.get(Constants.MAVEN_PROJECT_EXTENSIONS);
-        
extensions.addAll(readCoreExtensionsDescriptorFromFile(context.cwd.resolve(projectExtensionsFile)));
+        ArrayList<CoreExtension> projectExtensions =
+                new 
ArrayList<>(readCoreExtensionsDescriptorFromFile(context.cwd.resolve(projectExtensionsFile)));
 
-        String userExtensionsFile = 
context.userProperties.get(Constants.MAVEN_USER_EXTENSIONS);
-        
extensions.addAll(readCoreExtensionsDescriptorFromFile(context.userHomeDirectory.resolve(userExtensionsFile)));
+        // merge these 3 but check for GA uniqueness; we don't want to load up 
same extension w/ different Vs
+        HashMap<String, String> gas = new HashMap<>();
+        ArrayList<String> conflicts = new ArrayList<>();
+
+        ArrayList<CoreExtension> coreExtensions =
+                new ArrayList<>(installationExtensions.size() + 
userExtensions.size() + projectExtensions.size());
+        coreExtensions.addAll(mergeExtensions(installationExtensions, 
installationExtensionsFile, gas, conflicts));
+        coreExtensions.addAll(mergeExtensions(userExtensions, 
userExtensionsFile, gas, conflicts));
+        coreExtensions.addAll(mergeExtensions(projectExtensions, 
projectExtensionsFile, gas, conflicts));
+
+        if (!conflicts.isEmpty()) {
+            throw new ParserException("Extension conflicts: " + String.join("; 
", conflicts));
+        }
 
+        return coreExtensions;
+    }
+
+    private List<CoreExtension> mergeExtensions(
+            List<CoreExtension> extensions, String extensionsSource, 
Map<String, String> gas, List<String> conflicts) {
+        for (CoreExtension extension : extensions) {
+            String ga = extension.getGroupId() + ":" + 
extension.getArtifactId();
+            if (gas.containsKey(ga)) {
+                conflicts.add(ga + " from " + extensionsSource + " already 
specified in " + gas.get(ga));
+            } else {
+                gas.put(ga, extensionsSource);
+            }
+        }
         return extensions;
     }
 
diff --git 
a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java
 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java
index bae4f7015f..21a24bed57 100644
--- 
a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java
+++ 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java
@@ -19,6 +19,7 @@
 package org.apache.maven.cling.invoker.mvn;
 
 import java.nio.file.FileSystem;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Arrays;
 
@@ -26,6 +27,7 @@
 import com.google.common.jimfs.Jimfs;
 import org.apache.maven.api.cli.Invoker;
 import org.apache.maven.api.cli.Parser;
+import org.apache.maven.api.cli.ParserException;
 import org.apache.maven.cling.invoker.ProtoLookup;
 import org.codehaus.plexus.classworlds.ClassWorld;
 import org.junit.jupiter.api.Disabled;
@@ -34,6 +36,8 @@
 import org.junit.jupiter.api.io.CleanupMode;
 import org.junit.jupiter.api.io.TempDir;
 
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
 /**
  * Local UT.
  */
@@ -59,6 +63,35 @@ void defaultFs(
         invoke(cwd, userHome, Arrays.asList("clean", "verify"));
     }
 
+    @Test
+    void conflictingExtensions(
+            @TempDir(cleanup = CleanupMode.ON_SUCCESS) Path cwd,
+            @TempDir(cleanup = CleanupMode.ON_SUCCESS) Path userHome)
+            throws Exception {
+        String extensionsXml =
+                """
+                <?xml version="1.0" encoding="UTF-8"?>
+                <extensions>
+                    <extension>
+                        <groupId>eu.maveniverse.maven.mimir</groupId>
+                        <artifactId>extension3</artifactId>
+                        <version>0.3.4</version>
+                    </extension>
+                </extensions>
+                """;
+        Path dotMvn = cwd.resolve(".mvn");
+        Files.createDirectories(dotMvn);
+        Path projectExtensions = dotMvn.resolve("extensions.xml");
+        Files.writeString(projectExtensions, extensionsXml);
+
+        Path userConf = userHome.resolve(".m2");
+        Files.createDirectories(userConf);
+        Path userExtensions = userConf.resolve("extensions.xml");
+        Files.writeString(userExtensions, extensionsXml);
+
+        assertThrows(ParserException.class, () -> invoke(cwd, userHome, 
Arrays.asList("clean", "verify")));
+    }
+
     @Disabled("Until we move off fully from File")
     @Test
     void jimFs() throws Exception {
diff --git 
a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
index 89b33df73f..15caa92f67 100644
--- 
a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
+++ 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
@@ -88,7 +88,7 @@ protected void invoke(Path cwd, Path userHome, 
Collection<String> goals) throws
                         .resolve("maven.properties")),
                 "${maven.home}/conf/maven.properties must be a file");
 
-        Files.createDirectory(cwd.resolve(".mvn"));
+        Files.createDirectories(cwd.resolve(".mvn"));
         Path pom = cwd.resolve("pom.xml").toAbsolutePath();
         Files.writeString(pom, POM_STRING);
         Path appJava = 
cwd.resolve("src/main/java/org/apache/maven/samples/sample/App.java");

Reply via email to