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