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