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 }