You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by sj...@apache.org on 2024/04/18 21:32:54 UTC

(maven-dependency-plugin) branch MDEP-922 created (now 11ffd2f2)

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

sjaranowski pushed a change to branch MDEP-922
in repository https://gitbox.apache.org/repos/asf/maven-dependency-plugin.git


      at 11ffd2f2 [MDEP-922] dependency:analyze-exclusions - should report issue only in current project

This branch includes the following new commits:

     new 11ffd2f2 [MDEP-922] dependency:analyze-exclusions - should report issue only in current project

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(maven-dependency-plugin) 01/01: [MDEP-922] dependency:analyze-exclusions - should report issue only in current project

Posted by sj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sjaranowski pushed a commit to branch MDEP-922
in repository https://gitbox.apache.org/repos/asf/maven-dependency-plugin.git

commit 11ffd2f20f4829ed61131d61f2c37c2c9dfea65e
Author: Slawomir Jaranowski <s....@gmail.com>
AuthorDate: Thu Apr 18 23:32:33 2024 +0200

    [MDEP-922] dependency:analyze-exclusions - should report issue only in current project
    
    - filter exclusion by dependency location
---
 .../pom.xml => mrm/repository/a-with-dep.pom}      | 17 +++---
 .../pom.xml => mrm/repository/b-with-dep.pom}      | 18 +++---
 .../pom.xml => mrm/repository/c-without-dep.pom}   | 18 ++----
 .../module1/pom.xml                                | 19 +++++-
 .../module2/pom.xml                                |  4 +-
 .../pom.xml                                        | 10 ++--
 .../verify.groovy                                  | 36 ++++++++---
 src/it/projects/analyze-invalid-exclude/pom.xml    | 70 ++++++++++++----------
 .../projects/analyze-invalid-exclude/verify.groovy | 26 +++++---
 .../exclusion/AnalyzeExclusionsMojo.java           | 61 +++++++++++++++----
 .../plugins/dependency/exclusion/Coordinates.java  | 56 +++++++++++++++--
 .../dependency/exclusion/ExclusionChecker.java     |  5 +-
 .../exclusion/AnalyzeExclusionsMojoTest.java       | 11 ++++
 .../dependency/exclusion/ExclusionCheckerTest.java |  2 +-
 14 files changed, 242 insertions(+), 111 deletions(-)

diff --git a/src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml b/src/it/mrm/repository/a-with-dep.pom
similarity index 78%
copy from src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml
copy to src/it/mrm/repository/a-with-dep.pom
index 2b329db3..d3d163a8 100644
--- a/src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml
+++ b/src/it/mrm/repository/a-with-dep.pom
@@ -20,21 +20,18 @@
 
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
-  <parent>
-    <groupId>org.apache.maven.its.dependency</groupId>
-    <artifactId>test-parent</artifactId>
-    <version>1.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>test-module1</artifactId>
+  <groupId>org.apache.maven.its.dependency</groupId>
+  <artifactId>a-with-dep</artifactId>
+  <version>1.0.0</version>
 
   <dependencies>
     <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-core</artifactId>
+      <groupId>org.apache.maven.its.dependency</groupId>
+      <artifactId>b-with-dep</artifactId>
+      <version>1.0.0</version>
     </dependency>
   </dependencies>
 </project>
diff --git a/src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml b/src/it/mrm/repository/b-with-dep.pom
similarity index 78%
copy from src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml
copy to src/it/mrm/repository/b-with-dep.pom
index 2b329db3..60c213fe 100644
--- a/src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml
+++ b/src/it/mrm/repository/b-with-dep.pom
@@ -20,21 +20,19 @@
 
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
-  <parent>
-    <groupId>org.apache.maven.its.dependency</groupId>
-    <artifactId>test-parent</artifactId>
-    <version>1.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>test-module1</artifactId>
+  <groupId>org.apache.maven.its.dependency</groupId>
+  <artifactId>b-with-dep</artifactId>
+  <version>1.0.0</version>
 
   <dependencies>
     <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-core</artifactId>
+      <groupId>org.apache.maven.its.dependency</groupId>
+      <artifactId>c-without-dep</artifactId>
+      <version>1.0.0</version>
     </dependency>
   </dependencies>
+
 </project>
diff --git a/src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml b/src/it/mrm/repository/c-without-dep.pom
similarity index 73%
copy from src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml
copy to src/it/mrm/repository/c-without-dep.pom
index 2b329db3..36721d29 100644
--- a/src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml
+++ b/src/it/mrm/repository/c-without-dep.pom
@@ -20,21 +20,11 @@
 
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
-  <parent>
-    <groupId>org.apache.maven.its.dependency</groupId>
-    <artifactId>test-parent</artifactId>
-    <version>1.0-SNAPSHOT</version>
-  </parent>
+  <groupId>org.apache.maven.its.dependency</groupId>
+  <artifactId>c-without-dep</artifactId>
+  <version>1.0.0</version>
 
-  <artifactId>test-module1</artifactId>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-core</artifactId>
-    </dependency>
-  </dependencies>
 </project>
diff --git a/src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml b/src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml
index 2b329db3..fb266266 100644
--- a/src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml
+++ b/src/it/projects/analyze-invalid-exclude-multumodule-project/module1/pom.xml
@@ -33,8 +33,23 @@
 
   <dependencies>
     <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-core</artifactId>
+      <groupId>org.apache.maven.its.dependency</groupId>
+      <artifactId>a-with-dep</artifactId>
+      <version>1.0.0</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.maven.its.dependency</groupId>
+          <artifactId>invalid-exclusion2</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.apache.maven.its.dependency</groupId>
+          <artifactId>c-without-dep</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.apache.maven.its.dependency</groupId>
+          <artifactId>invalid-exclusion3</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
   </dependencies>
 </project>
diff --git a/src/it/projects/analyze-invalid-exclude-multumodule-project/module2/pom.xml b/src/it/projects/analyze-invalid-exclude-multumodule-project/module2/pom.xml
index 17d63f14..59f3a92b 100644
--- a/src/it/projects/analyze-invalid-exclude-multumodule-project/module2/pom.xml
+++ b/src/it/projects/analyze-invalid-exclude-multumodule-project/module2/pom.xml
@@ -38,8 +38,8 @@
       <version>${project.version}</version>
       <exclusions>
         <exclusion>
-          <groupId>org.apache.maven</groupId>
-          <artifactId>maven-core</artifactId>
+          <groupId>org.apache.maven.its.dependency</groupId>
+          <artifactId>a-with-dep</artifactId>
         </exclusion>
       </exclusions>
     </dependency>
diff --git a/src/it/projects/analyze-invalid-exclude-multumodule-project/pom.xml b/src/it/projects/analyze-invalid-exclude-multumodule-project/pom.xml
index 56e37b4c..8e9c27a9 100644
--- a/src/it/projects/analyze-invalid-exclude-multumodule-project/pom.xml
+++ b/src/it/projects/analyze-invalid-exclude-multumodule-project/pom.xml
@@ -45,13 +45,13 @@
   <dependencyManagement>
     <dependencies>
       <dependency>
-        <groupId>org.apache.maven</groupId>
-        <artifactId>maven-core</artifactId>
-        <version>3.9.6</version>
+        <groupId>org.apache.maven.its.dependency</groupId>
+        <artifactId>a-with-dep</artifactId>
+        <version>1.0.0</version>
         <exclusions>
           <exclusion>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
+            <groupId>org.apache.maven.its.dependency</groupId>
+            <artifactId>invalid-exclusion1</artifactId>
           </exclusion>
         </exclusions>
       </dependency>
diff --git a/src/it/projects/analyze-invalid-exclude-multumodule-project/verify.groovy b/src/it/projects/analyze-invalid-exclude-multumodule-project/verify.groovy
index 75051429..8b798d26 100644
--- a/src/it/projects/analyze-invalid-exclude-multumodule-project/verify.groovy
+++ b/src/it/projects/analyze-invalid-exclude-multumodule-project/verify.groovy
@@ -1,3 +1,5 @@
+import java.lang.reflect.Array
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -17,14 +19,34 @@
  * under the License.
  */
 
-File file = new File( basedir, "build.log" );
+
+static void checkMessagedInLogs(logLines, messages) {
+    def index = logLines.indexOf(messages[0])
+    assert index > 0: "no messages: '" + messages[0] + "' in log"
+
+    def logMessages = logLines[index..index + messages.size() - 1]
+
+    assert logMessages == messages
+
+}
+
+def file = new File(basedir, "build.log");
 assert file.exists();
 
-String buildLog = file.getText( "UTF-8" );
-assert buildLog.contains( '[WARNING] test-module1 defines following unnecessary excludes');
-assert buildLog.contains( '[WARNING]     org.apache.maven:maven-core:');
-assert buildLog.contains( '[WARNING]         - javax.servlet:javax.servlet-api');
+def logLines = buildLog = file.readLines()
+
+checkMessagedInLogs(logLines, [
+        '[WARNING] Test defines following unnecessary excludes',
+        '[WARNING]     org.apache.maven.its.dependency:a-with-dep:1.0.0',
+        '[WARNING]         - org.apache.maven.its.dependency:invalid-exclusion1 @ line: 52'
+])
+
+checkMessagedInLogs(logLines, [
+        '[WARNING] test-module1 defines following unnecessary excludes',
+        '[WARNING]     org.apache.maven.its.dependency:a-with-dep:1.0.0',
+        '[WARNING]         - org.apache.maven.its.dependency:invalid-exclusion2 @ line: 40',
+        '[WARNING]         - org.apache.maven.its.dependency:invalid-exclusion3 @ line: 48'
 
-assert !buildLog.contains( '[WARNING] test-module2 defines following unnecessary excludes');
+])
 
-return true;
+assert logLines.count('[INFO] No problems with dependencies exclusions') == 1
diff --git a/src/it/projects/analyze-invalid-exclude/pom.xml b/src/it/projects/analyze-invalid-exclude/pom.xml
index 17587fea..5c65025c 100644
--- a/src/it/projects/analyze-invalid-exclude/pom.xml
+++ b/src/it/projects/analyze-invalid-exclude/pom.xml
@@ -36,53 +36,59 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>
 
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.apache.maven.its.dependency</groupId>
+        <artifactId>a-with-dep</artifactId>
+        <version>1.0.0</version>
+        <exclusions>
+          <exclusion>
+            <groupId>org.apache.maven.its.dependency</groupId>
+            <artifactId>invalid-exclusion1</artifactId>
+          </exclusion>
+        </exclusions>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
   <dependencies>
     <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-core</artifactId>
+      <groupId>org.apache.maven.its.dependency</groupId>
+      <artifactId>b-with-dep</artifactId>
+      <version>1.0.0</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.maven.its.dependency</groupId>
+          <artifactId>c-without-dep</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.apache.maven.its.dependency</groupId>
+          <artifactId>invalid-exclusion3</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-artifact</artifactId>
-      <version>3.9.6</version>
+      <groupId>org.apache.maven.its.dependency</groupId>
+      <artifactId>a-with-dep</artifactId>
       <exclusions>
         <exclusion>
-          <groupId>javax.annotation</groupId>
-          <artifactId>javax.annotation-api</artifactId>
+          <groupId>org.apache.maven.its.dependency</groupId>
+          <artifactId>invalid-exclusion2</artifactId>
         </exclusion>
         <exclusion>
-          <groupId>javax.activation</groupId>
-          <artifactId>javax.activation-api</artifactId>
+          <groupId>org.apache.maven.its.dependency</groupId>
+          <artifactId>c-without-dep</artifactId>
         </exclusion>
       </exclusions>
     </dependency>
     <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-model</artifactId>
-      <version>3.9.6</version>
+      <groupId>org.apache.maven.its.dependency</groupId>
+      <artifactId>c-without-dep</artifactId>
+      <version>1.0.0</version>
     </dependency>
   </dependencies>
 
-  <dependencyManagement>
-    <dependencies>
-      <dependency>
-        <groupId>org.apache.maven</groupId>
-        <artifactId>maven-core</artifactId>
-        <version>3.9.6</version>
-        <exclusions>
-          <exclusion>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
-          </exclusion>
-          <exclusion>
-            <groupId>org.codehaus.plexus</groupId>
-            <artifactId>*</artifactId>
-          </exclusion>
-        </exclusions>
-      </dependency>
-    </dependencies>
-  </dependencyManagement>
-
   <build>
     <pluginManagement>
       <plugins>
diff --git a/src/it/projects/analyze-invalid-exclude/verify.groovy b/src/it/projects/analyze-invalid-exclude/verify.groovy
index 513517d1..fb3c8c7a 100644
--- a/src/it/projects/analyze-invalid-exclude/verify.groovy
+++ b/src/it/projects/analyze-invalid-exclude/verify.groovy
@@ -17,15 +17,23 @@
  * under the License.
  */
 
-File file = new File( basedir, "build.log" );
+def file = new File(basedir, "build.log");
 assert file.exists();
 
-String buildLog = file.getText( "UTF-8" );
-assert buildLog.contains( '[WARNING] test-module defines following unnecessary excludes');
-assert buildLog.contains( '[WARNING]     org.apache.maven:maven-artifact:');
-assert buildLog.contains( '[WARNING]         - javax.annotation:javax.annotation-api');
-assert buildLog.contains( '[WARNING]         - javax.activation:javax.activation-api');
-assert buildLog.contains( '[WARNING]     org.apache.maven:maven-core:');
-assert buildLog.contains( '[WARNING]         - javax.servlet:javax.servlet-api');
+def logLines = buildLog = file.readLines()
+
+def index = logLines.indexOf('[WARNING] test-module defines following unnecessary excludes')
+assert index > 0: "no messages in log"
+
+def messages = logLines[index..index + 5];
+
+assert messages == [
+        '[WARNING] test-module defines following unnecessary excludes',
+        '[WARNING]     org.apache.maven.its.dependency:a-with-dep:1.0.0',
+        '[WARNING]         - org.apache.maven.its.dependency:invalid-exclusion1 @ line: 46',
+        '[WARNING]         - org.apache.maven.its.dependency:invalid-exclusion2 @ line: 75',
+        '[WARNING]     org.apache.maven.its.dependency:b-with-dep:1.0.0',
+        '[WARNING]         - org.apache.maven.its.dependency:invalid-exclusion3 @ line: 65'
+]
+
 
-return true;
diff --git a/src/main/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojo.java b/src/main/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojo.java
index 871296f6..3ad8d3b9 100644
--- a/src/main/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojo.java
+++ b/src/main/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojo.java
@@ -18,7 +18,9 @@
  */
 package org.apache.maven.plugins.dependency.exclusion;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -27,6 +29,7 @@ import java.util.function.Consumer;
 import org.apache.maven.RepositoryUtils;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Exclusion;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugins.annotations.Component;
@@ -79,15 +82,39 @@ public class AnalyzeExclusionsMojo extends AbstractMojo {
     @Parameter(property = "mdep.skip", defaultValue = "false")
     private boolean skip;
 
+    /**
+     * Current project modelId.
+     */
+    private String projectModelId;
+
     @Override
     public void execute() throws MojoExecutionException {
         if (skip) {
             getLog().debug("Skipping execution");
             return;
         }
-        Collection<Dependency> dependenciesWithExclusions = project.getDependencies().stream()
-                .filter(dep -> !dep.getExclusions().isEmpty())
-                .collect(toList());
+
+        projectModelId = project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion();
+
+        Map<Coordinates, Collection<Exclusion>> dependenciesWithExclusions = new HashMap<>();
+
+        project.getDependencyManagement().getDependencies().forEach(dependency -> {
+            Collection<Exclusion> exclusions = getExclusionsForDependency(dependency);
+            if (!exclusions.isEmpty()) {
+                dependenciesWithExclusions
+                        .computeIfAbsent(coordinates(dependency), d -> new ArrayList<>())
+                        .addAll(exclusions);
+            }
+        });
+
+        project.getDependencies().forEach(dependency -> {
+            Collection<Exclusion> exclusions = getExclusionsForDependency(dependency);
+            if (!exclusions.isEmpty()) {
+                dependenciesWithExclusions
+                        .computeIfAbsent(coordinates(dependency), d -> new ArrayList<>())
+                        .addAll(exclusions);
+            }
+        });
 
         if (dependenciesWithExclusions.isEmpty()) {
             getLog().debug("No dependencies defined with exclusions - exiting");
@@ -98,14 +125,14 @@ public class AnalyzeExclusionsMojo extends AbstractMojo {
 
         ArtifactTypeRegistry artifactTypeRegistry =
                 session.getRepositorySession().getArtifactTypeRegistry();
-        for (final Dependency dependency : dependenciesWithExclusions) {
+        for (Map.Entry<Coordinates, Collection<Exclusion>> entry : dependenciesWithExclusions.entrySet()) {
 
-            Coordinates currentCoordinates = coordinates(dependency.getGroupId(), dependency.getArtifactId());
+            Coordinates currentCoordinates = entry.getKey();
 
             Collection<org.eclipse.aether.graph.Dependency> actualDependencies = null;
             try {
-                actualDependencies =
-                        resolverUtil.collectDependencies(RepositoryUtils.toDependency(dependency, artifactTypeRegistry)
+                actualDependencies = resolverUtil.collectDependencies(
+                        RepositoryUtils.toDependency(currentCoordinates.getDependency(), artifactTypeRegistry)
                                 .setExclusions(null));
             } catch (DependencyCollectionException e) {
                 throw new MojoExecutionException(e.getMessage(), e);
@@ -116,9 +143,8 @@ public class AnalyzeExclusionsMojo extends AbstractMojo {
                     .map(a -> coordinates(a.getGroupId(), a.getArtifactId()))
                     .collect(toSet());
 
-            Set<Coordinates> exclusions = dependency.getExclusions().stream()
-                    .map(e -> coordinates(e.getGroupId(), e.getArtifactId()))
-                    .collect(toSet());
+            Set<Coordinates> exclusions =
+                    entry.getValue().stream().map(Coordinates::coordinates).collect(toSet());
 
             checker.check(currentCoordinates, exclusions, actualCoordinates);
         }
@@ -130,13 +156,26 @@ public class AnalyzeExclusionsMojo extends AbstractMojo {
             } else {
                 logViolations(project.getName(), checker.getViolations(), value -> getLog().warn(value));
             }
+        } else {
+            getLog().info("No problems with dependencies exclusions");
         }
     }
 
+    private Collection<Exclusion> getExclusionsForDependency(Dependency dependency) {
+        return dependency.getExclusions().stream()
+                .filter(this::isExclusionInProject)
+                .collect(toList());
+    }
+
+    private boolean isExclusionInProject(Exclusion exclusion) {
+        String modelId = exclusion.getLocation("").getSource().getModelId();
+        return projectModelId.equals(modelId);
+    }
+
     private void logViolations(String name, Map<Coordinates, List<Coordinates>> violations, Consumer<String> logger) {
         logger.accept(name + " defines following unnecessary excludes");
         violations.forEach((dependency, invalidExclusions) -> {
-            logger.accept("    " + dependency + ":");
+            logger.accept("    " + dependency);
             invalidExclusions.forEach(invalidExclusion -> logger.accept("        - " + invalidExclusion));
         });
     }
diff --git a/src/main/java/org/apache/maven/plugins/dependency/exclusion/Coordinates.java b/src/main/java/org/apache/maven/plugins/dependency/exclusion/Coordinates.java
index cb4a042e..d81f7b37 100644
--- a/src/main/java/org/apache/maven/plugins/dependency/exclusion/Coordinates.java
+++ b/src/main/java/org/apache/maven/plugins/dependency/exclusion/Coordinates.java
@@ -25,23 +25,43 @@ import java.nio.file.PathMatcher;
 import java.util.Objects;
 import java.util.function.Predicate;
 
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Exclusion;
+import org.apache.maven.model.InputLocation;
+
 /**
  * Simple "record" to hold the coordinates of the dependency which is excluded.
  * <p>
  * When dealing with exclusions the version is not important. Only groupId and artifactId is used.
  * </p>
  */
-class Coordinates {
+class Coordinates implements Comparable<Coordinates> {
+
     private final String groupId;
+
     private final String artifactId;
 
-    private Coordinates(String groupId, String artifactId) {
+    private final Dependency dependency;
+
+    private final InputLocation location;
+
+    private Coordinates(String groupId, String artifactId, Dependency dependency, InputLocation location) {
         this.groupId = groupId;
         this.artifactId = artifactId;
+        this.dependency = dependency;
+        this.location = location;
     }
 
     public static Coordinates coordinates(String groupId, String artifactId) {
-        return new Coordinates(groupId, artifactId);
+        return new Coordinates(groupId, artifactId, null, null);
+    }
+
+    public static Coordinates coordinates(Dependency dependency) {
+        return new Coordinates(dependency.getGroupId(), dependency.getArtifactId(), dependency, null);
+    }
+
+    public static Coordinates coordinates(Exclusion exclusion) {
+        return new Coordinates(exclusion.getGroupId(), exclusion.getArtifactId(), null, exclusion.getLocation(""));
     }
 
     public String getGroupId() {
@@ -52,6 +72,10 @@ class Coordinates {
         return artifactId;
     }
 
+    public Dependency getDependency() {
+        return dependency;
+    }
+
     Predicate<Coordinates> getExclusionPattern() {
         PathMatcher groupId = FileSystems.getDefault().getPathMatcher("glob:" + getGroupId());
         PathMatcher artifactId = FileSystems.getDefault().getPathMatcher("glob:" + getArtifactId());
@@ -86,16 +110,36 @@ class Coordinates {
             return false;
         }
         Coordinates that = (Coordinates) o;
-        return Objects.equals(groupId, that.groupId) && Objects.equals(artifactId, that.artifactId);
+        return Objects.equals(groupId, that.groupId)
+                && Objects.equals(artifactId, that.artifactId)
+                && Objects.equals(location, that.location);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(groupId, artifactId);
+        return Objects.hash(groupId, artifactId, location);
+    }
+
+    @Override
+    public int compareTo(Coordinates that) {
+
+        if (location != null && that.location != null) {
+            return location.getLineNumber() - that.location.getLineNumber();
+        }
+
+        return toString().compareTo(that.toString());
     }
 
     @Override
     public String toString() {
-        return groupId + ":" + artifactId;
+        String version = "";
+        if (dependency != null) {
+            version = ":" + dependency.getVersion();
+        }
+        String line = "";
+        if (location != null) {
+            line = " @ line: " + location.getLineNumber();
+        }
+        return groupId + ":" + artifactId + version + line;
     }
 }
diff --git a/src/main/java/org/apache/maven/plugins/dependency/exclusion/ExclusionChecker.java b/src/main/java/org/apache/maven/plugins/dependency/exclusion/ExclusionChecker.java
index 7219cdee..1340461b 100644
--- a/src/main/java/org/apache/maven/plugins/dependency/exclusion/ExclusionChecker.java
+++ b/src/main/java/org/apache/maven/plugins/dependency/exclusion/ExclusionChecker.java
@@ -18,16 +18,16 @@
  */
 package org.apache.maven.plugins.dependency.exclusion;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 
 import static java.util.stream.Collectors.toList;
 
 class ExclusionChecker {
 
-    private final Map<Coordinates, List<Coordinates>> violations = new HashMap<>();
+    private final Map<Coordinates, List<Coordinates>> violations = new TreeMap<>();
 
     Map<Coordinates, List<Coordinates>> getViolations() {
         return violations;
@@ -36,6 +36,7 @@ class ExclusionChecker {
     void check(Coordinates artifact, Set<Coordinates> excludes, Set<Coordinates> actualDependencies) {
         List<Coordinates> invalidExclusions = excludes.stream()
                 .filter(exclude -> actualDependencies.stream().noneMatch(exclude.getExclusionPattern()))
+                .sorted()
                 .collect(toList());
 
         if (!invalidExclusions.isEmpty()) {
diff --git a/src/test/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojoTest.java b/src/test/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojoTest.java
index 10ce1844..231871cf 100644
--- a/src/test/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojoTest.java
+++ b/src/test/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojoTest.java
@@ -32,6 +32,8 @@ import org.apache.maven.artifact.Artifact;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.model.Exclusion;
+import org.apache.maven.model.InputLocation;
+import org.apache.maven.model.InputSource;
 import org.apache.maven.plugin.LegacySupport;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.logging.Log;
@@ -63,6 +65,10 @@ public class AnalyzeExclusionsMojoTest extends AbstractDependencyMojoTestCase {
 
         project = new DependencyProjectStub();
         project.setName("projectName");
+        project.setGroupId("testGroupId");
+        project.setArtifactId("testArtifactId");
+        project.setVersion("1.0.0");
+
         getContainer().addComponent(project, MavenProject.class.getName());
 
         MavenSession session = newMavenSession(project);
@@ -192,6 +198,7 @@ public class AnalyzeExclusionsMojoTest extends AbstractDependencyMojoTestCase {
         dependency.setScope("compile");
         dependency.setType("jar");
         dependency.setClassifier("");
+        dependency.setLocation("", new InputLocation(1, 1));
         return dependency;
     }
 
@@ -203,6 +210,7 @@ public class AnalyzeExclusionsMojoTest extends AbstractDependencyMojoTestCase {
         dependency.setScope(scope);
         dependency.setType("jar");
         dependency.setClassifier(classifier);
+        dependency.setLocation("", new InputLocation(1, 1));
         return dependency;
     }
 
@@ -210,6 +218,9 @@ public class AnalyzeExclusionsMojoTest extends AbstractDependencyMojoTestCase {
         Exclusion exclusion = new Exclusion();
         exclusion.setGroupId(groupId);
         exclusion.setArtifactId(artifactId);
+        InputSource inputSource = new InputSource();
+        inputSource.setModelId("testGroupId:testArtifactId:1.0.0");
+        exclusion.setLocation("", new InputLocation(1, 1, inputSource));
         return exclusion;
     }
 
diff --git a/src/test/java/org/apache/maven/plugins/dependency/exclusion/ExclusionCheckerTest.java b/src/test/java/org/apache/maven/plugins/dependency/exclusion/ExclusionCheckerTest.java
index 857fd6f6..70a42411 100644
--- a/src/test/java/org/apache/maven/plugins/dependency/exclusion/ExclusionCheckerTest.java
+++ b/src/test/java/org/apache/maven/plugins/dependency/exclusion/ExclusionCheckerTest.java
@@ -54,7 +54,7 @@ public class ExclusionCheckerTest {
         assertThat(checker.getViolations())
                 .containsEntry(
                         artifact,
-                        Arrays.asList(coordinates("com.example", "two"), coordinates("com.example", "three")));
+                        Arrays.asList(coordinates("com.example", "three"), coordinates("com.example", "two")));
     }
 
     @Test