1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  package org.apache.maven.plugins.dependency.analyze;
20  
21  import javax.inject.Inject;
22  
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.LinkedHashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.stream.Collectors;
30  
31  import org.apache.maven.artifact.Artifact;
32  import org.apache.maven.model.Dependency;
33  import org.apache.maven.model.DependencyManagement;
34  import org.apache.maven.model.Exclusion;
35  import org.apache.maven.plugin.AbstractMojo;
36  import org.apache.maven.plugin.MojoExecutionException;
37  import org.apache.maven.plugin.MojoFailureException;
38  import org.apache.maven.plugins.annotations.Mojo;
39  import org.apache.maven.plugins.annotations.Parameter;
40  import org.apache.maven.plugins.annotations.ResolutionScope;
41  import org.apache.maven.project.MavenProject;
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  @Mojo(name = "analyze-dep-mgt", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true)
52  public class AnalyzeDepMgt extends AbstractMojo {
53      
54  
55      private final MavenProject project;
56  
57      
58  
59  
60      @Parameter(property = "mdep.analyze.failBuild", defaultValue = "false")
61      private boolean failBuild = false;
62  
63      
64  
65  
66      @Parameter(property = "mdep.analyze.ignore.direct", defaultValue = "true")
67      private boolean ignoreDirect = true;
68  
69      
70  
71  
72  
73  
74      @Parameter(property = "mdep.analyze.skip", defaultValue = "false")
75      private boolean skip;
76  
77      @Inject
78      public AnalyzeDepMgt(MavenProject project) {
79          this.project = project;
80      }
81  
82      
83  
84      
85  
86  
87      @Override
88      public void execute() throws MojoExecutionException, MojoFailureException {
89          if (skip) {
90              getLog().info("Skipping plugin execution");
91              return;
92          }
93  
94          boolean result = checkDependencyManagement();
95          if (result) {
96              if (this.failBuild) {
97  
98                  throw new MojoExecutionException("Found Dependency errors.");
99              } else {
100                 getLog().warn("Potential problems found in Dependency Management ");
101             }
102         }
103     }
104 
105     
106 
107 
108 
109 
110 
111     private boolean checkDependencyManagement() throws MojoExecutionException {
112         boolean foundError = false;
113 
114         getLog().info("Found Resolved Dependency/DependencyManagement mismatches:");
115 
116         List<Dependency> depMgtDependencies = null;
117 
118         DependencyManagement depMgt = project.getDependencyManagement();
119         if (depMgt != null) {
120             depMgtDependencies = depMgt.getDependencies();
121         }
122 
123         if (depMgtDependencies != null && !depMgtDependencies.isEmpty()) {
124             
125             Map<String, Dependency> depMgtMap = new HashMap<>();
126             Map<String, Exclusion> exclusions = new HashMap<>();
127             for (Dependency depMgtDependency : depMgtDependencies) {
128                 depMgtMap.put(depMgtDependency.getManagementKey(), depMgtDependency);
129 
130                 
131                 exclusions.putAll(addExclusions(depMgtDependency.getExclusions()));
132             }
133 
134             
135             Set<Artifact> allDependencyArtifacts = new LinkedHashSet<>(project.getArtifacts());
136 
137             
138             
139             if (this.ignoreDirect) {
140                 getLog().info("\tIgnoring Direct Dependencies.");
141                 Set<Artifact> directDependencies = project.getDependencyArtifacts();
142                 allDependencyArtifacts.removeAll(directDependencies);
143             }
144 
145             
146             List<Artifact> exclusionErrors = getExclusionErrors(exclusions, allDependencyArtifacts);
147             for (Artifact exclusion : exclusionErrors) {
148                 getLog().info(getArtifactManagementKey(exclusion)
149                         + " was excluded in DepMgt, but version " + exclusion.getVersion()
150                         + " has been found in the dependency tree.");
151                 foundError = true;
152             }
153 
154             
155             Map<Artifact, Dependency> mismatch = getMismatch(depMgtMap, allDependencyArtifacts);
156             for (Map.Entry<Artifact, Dependency> entry : mismatch.entrySet()) {
157                 logMismatch(entry.getKey(), entry.getValue());
158                 foundError = true;
159             }
160             if (!foundError) {
161                 getLog().info("\tNone");
162             }
163         } else {
164             getLog().info("\tNothing in DepMgt.");
165         }
166 
167         return foundError;
168     }
169 
170     
171 
172 
173 
174 
175 
176     public Map<String, Exclusion> addExclusions(List<Exclusion> exclusionList) {
177         if (exclusionList != null) {
178             return exclusionList.stream().collect(Collectors.toMap(this::getExclusionKey, exclusion -> exclusion));
179         }
180         return Collections.emptyMap();
181     }
182 
183     
184 
185 
186 
187 
188 
189 
190 
191     public List<Artifact> getExclusionErrors(Map<String, Exclusion> exclusions, Set<Artifact> allDependencyArtifacts) {
192         return allDependencyArtifacts.stream()
193                 .filter(artifact -> exclusions.containsKey(getExclusionKey(artifact)))
194                 .collect(Collectors.toList());
195     }
196 
197     
198 
199 
200 
201     public String getExclusionKey(Artifact artifact) {
202         return artifact.getGroupId() + ":" + artifact.getArtifactId();
203     }
204 
205     
206 
207 
208 
209     public String getExclusionKey(Exclusion ex) {
210         return ex.getGroupId() + ":" + ex.getArtifactId();
211     }
212 
213     
214 
215 
216 
217 
218 
219 
220     public Map<Artifact, Dependency> getMismatch(
221             Map<String, Dependency> depMgtMap, Set<Artifact> allDependencyArtifacts) {
222         Map<Artifact, Dependency> mismatchMap = new HashMap<>();
223 
224         for (Artifact dependencyArtifact : allDependencyArtifacts) {
225             Dependency depFromDepMgt = depMgtMap.get(getArtifactManagementKey(dependencyArtifact));
226             if (depFromDepMgt != null) {
227                 if (depFromDepMgt.getVersion() != null
228                         && !depFromDepMgt.getVersion().equals(dependencyArtifact.getBaseVersion())) {
229                     mismatchMap.put(dependencyArtifact, depFromDepMgt);
230                 }
231             }
232         }
233         return mismatchMap;
234     }
235 
236     
237 
238 
239 
240 
241 
242 
243 
244     public void logMismatch(Artifact dependencyArtifact, Dependency dependencyFromDepMgt)
245             throws MojoExecutionException {
246         if (dependencyArtifact == null || dependencyFromDepMgt == null) {
247             throw new MojoExecutionException(
248                     "Invalid params: Artifact: " + dependencyArtifact + " Dependency: " + dependencyFromDepMgt);
249         }
250 
251         getLog().info("\tDependency: " + dependencyFromDepMgt.getManagementKey());
252         getLog().info("\t\tDepMgt  : " + dependencyFromDepMgt.getVersion());
253         getLog().info("\t\tResolved: " + dependencyArtifact.getBaseVersion());
254     }
255 
256     
257 
258 
259 
260 
261 
262     public String getArtifactManagementKey(Artifact artifact) {
263         return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType()
264                 + ((artifact.getClassifier() != null) ? ":" + artifact.getClassifier() : "");
265     }
266 
267     
268 
269 
270     protected final boolean isFailBuild() {
271         return this.failBuild;
272     }
273 
274     
275 
276 
277     public void setFailBuild(boolean theFailBuild) {
278         this.failBuild = theFailBuild;
279     }
280 
281     
282 
283 
284     protected final MavenProject getProject() {
285         return this.project;
286     }
287 
288     
289 
290 
291     protected final boolean isIgnoreDirect() {
292         return this.ignoreDirect;
293     }
294 
295     
296 
297 
298     public void setIgnoreDirect(boolean theIgnoreDirect) {
299         this.ignoreDirect = theIgnoreDirect;
300     }
301 }