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.io.File;
23 import java.io.StringWriter;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Iterator;
27 import java.util.LinkedHashSet;
28 import java.util.List;
29 import java.util.Set;
30
31 import org.apache.commons.lang.StringUtils;
32 import org.apache.maven.artifact.Artifact;
33 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
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.Parameter;
38 import org.apache.maven.project.MavenProject;
39 import org.apache.maven.shared.artifact.filter.StrictPatternExcludesArtifactFilter;
40 import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalysis;
41 import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzer;
42 import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzerException;
43 import org.codehaus.plexus.PlexusConstants;
44 import org.codehaus.plexus.PlexusContainer;
45 import org.codehaus.plexus.context.Context;
46 import org.codehaus.plexus.context.ContextException;
47 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
48 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
49
50
51
52
53
54
55
56
57 public abstract class AbstractAnalyzeMojo
58 extends AbstractMojo
59 implements Contextualizable
60 {
61
62
63
64
65
66
67 private Context context;
68
69
70
71
72 @Parameter( defaultValue = "${project}", readonly = true, required = true )
73 private MavenProject project;
74
75
76
77
78
79
80
81
82
83 @Parameter( property = "analyzer", defaultValue = "default" )
84 private String analyzer;
85
86
87
88
89 @Parameter( property = "failOnWarning", defaultValue = "false" )
90 private boolean failOnWarning;
91
92
93
94
95 @Parameter( property = "verbose", defaultValue = "false" )
96 private boolean verbose;
97
98
99
100
101 @Parameter( property = "ignoreNonCompile", defaultValue = "false" )
102 private boolean ignoreNonCompile;
103
104
105
106
107
108
109 @Parameter( property = "outputXML", defaultValue = "false" )
110 private boolean outputXML;
111
112
113
114
115
116
117 @Parameter( property = "scriptableOutput", defaultValue = "false" )
118 private boolean scriptableOutput;
119
120
121
122
123
124
125 @Parameter( property = "scriptableFlag", defaultValue = "$$$%%%" )
126 private String scriptableFlag;
127
128
129
130
131
132
133 @Parameter( defaultValue = "${basedir}", readonly = true )
134 private File baseDir;
135
136
137
138
139
140
141 @Parameter( defaultValue = "${project.build.directory}", readonly = true )
142 private File outputDirectory;
143
144
145
146
147
148
149
150 @Parameter
151 private String[] usedDependencies;
152
153
154
155
156
157
158 @Parameter( property = "mdep.analyze.skip", defaultValue = "false" )
159 private boolean skip;
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178 @Parameter
179 private String[] ignoredDependencies = new String[0];
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197 @Parameter
198 private String[] ignoredUsedUndeclaredDependencies = new String[0];
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216 @Parameter
217 private String[] ignoredUnusedDeclaredDependencies = new String[0];
218
219
220
221
222
223
224 @Override
225 public void execute()
226 throws MojoExecutionException, MojoFailureException
227 {
228 if ( isSkip() )
229 {
230 getLog().info( "Skipping plugin execution" );
231 return;
232 }
233
234 if ( "pom".equals( project.getPackaging() ) )
235 {
236 getLog().info( "Skipping pom project" );
237 return;
238 }
239
240 if ( outputDirectory == null || !outputDirectory.exists() )
241 {
242 getLog().info( "Skipping project with no build directory" );
243 return;
244 }
245
246 boolean warning = checkDependencies();
247
248 if ( warning && failOnWarning )
249 {
250 throw new MojoExecutionException( "Dependency problems found" );
251 }
252 }
253
254
255
256
257
258 protected ProjectDependencyAnalyzer createProjectDependencyAnalyzer()
259 throws MojoExecutionException
260 {
261
262 final String role = ProjectDependencyAnalyzer.ROLE;
263 final String roleHint = analyzer;
264
265 try
266 {
267 final PlexusContainer container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
268
269 return (ProjectDependencyAnalyzer) container.lookup( role, roleHint );
270 }
271 catch ( Exception exception )
272 {
273 throw new MojoExecutionException( "Failed to instantiate ProjectDependencyAnalyser with role " + role
274 + " / role-hint " + roleHint, exception );
275 }
276 }
277
278 @Override
279 public void contextualize( Context theContext )
280 throws ContextException
281 {
282 this.context = theContext;
283 }
284
285
286
287
288 protected final boolean isSkip()
289 {
290 return skip;
291 }
292
293
294
295 private boolean checkDependencies()
296 throws MojoExecutionException
297 {
298 ProjectDependencyAnalysis analysis;
299 try
300 {
301 analysis = createProjectDependencyAnalyzer().analyze( project );
302
303 if ( usedDependencies != null )
304 {
305 analysis = analysis.forceDeclaredDependenciesUsage( usedDependencies );
306 }
307 }
308 catch ( ProjectDependencyAnalyzerException exception )
309 {
310 throw new MojoExecutionException( "Cannot analyze dependencies", exception );
311 }
312
313 if ( ignoreNonCompile )
314 {
315 analysis = analysis.ignoreNonCompile();
316 }
317
318 Set<Artifact> usedDeclared = new LinkedHashSet<>( analysis.getUsedDeclaredArtifacts() );
319 Set<Artifact> usedUndeclared = new LinkedHashSet<>( analysis.getUsedUndeclaredArtifacts() );
320 Set<Artifact> unusedDeclared = new LinkedHashSet<>( analysis.getUnusedDeclaredArtifacts() );
321
322 Set<Artifact> ignoredUsedUndeclared = new LinkedHashSet<>();
323 Set<Artifact> ignoredUnusedDeclared = new LinkedHashSet<>();
324
325 ignoredUsedUndeclared.addAll( filterDependencies( usedUndeclared, ignoredDependencies ) );
326 ignoredUsedUndeclared.addAll( filterDependencies( usedUndeclared, ignoredUsedUndeclaredDependencies ) );
327
328 ignoredUnusedDeclared.addAll( filterDependencies( unusedDeclared, ignoredDependencies ) );
329 ignoredUnusedDeclared.addAll( filterDependencies( unusedDeclared, ignoredUnusedDeclaredDependencies ) );
330
331 boolean reported = false;
332 boolean warning = false;
333
334 if ( verbose && !usedDeclared.isEmpty() )
335 {
336 getLog().info( "Used declared dependencies found:" );
337
338 logArtifacts( analysis.getUsedDeclaredArtifacts(), false );
339 reported = true;
340 }
341
342 if ( !usedUndeclared.isEmpty() )
343 {
344 getLog().warn( "Used undeclared dependencies found:" );
345
346 logArtifacts( usedUndeclared, true );
347 reported = true;
348 warning = true;
349 }
350
351 if ( !unusedDeclared.isEmpty() )
352 {
353 getLog().warn( "Unused declared dependencies found:" );
354
355 logArtifacts( unusedDeclared, true );
356 reported = true;
357 warning = true;
358 }
359
360 if ( verbose && !ignoredUsedUndeclared.isEmpty() )
361 {
362 getLog().info( "Ignored used undeclared dependencies:" );
363
364 logArtifacts( ignoredUsedUndeclared, false );
365 reported = true;
366 }
367
368 if ( verbose && !ignoredUnusedDeclared.isEmpty() )
369 {
370 getLog().info( "Ignored unused declared dependencies:" );
371
372 logArtifacts( ignoredUnusedDeclared, false );
373 reported = true;
374 }
375
376 if ( outputXML )
377 {
378 writeDependencyXML( usedUndeclared );
379 }
380
381 if ( scriptableOutput )
382 {
383 writeScriptableOutput( usedUndeclared );
384 }
385
386 if ( !reported )
387 {
388 getLog().info( "No dependency problems found" );
389 }
390
391 return warning;
392 }
393
394 private void logArtifacts( Set<Artifact> artifacts, boolean warn )
395 {
396 if ( artifacts.isEmpty() )
397 {
398 getLog().info( " None" );
399 }
400 else
401 {
402 for ( Artifact artifact : artifacts )
403 {
404
405 artifact.isSnapshot();
406
407 if ( warn )
408 {
409 getLog().warn( " " + artifact );
410 }
411 else
412 {
413 getLog().info( " " + artifact );
414 }
415
416 }
417 }
418 }
419
420 private void writeDependencyXML( Set<Artifact> artifacts )
421 {
422 if ( !artifacts.isEmpty() )
423 {
424 getLog().info( "Add the following to your pom to correct the missing dependencies: " );
425
426 StringWriter out = new StringWriter();
427 PrettyPrintXMLWriter writer = new PrettyPrintXMLWriter( out );
428
429 for ( Artifact artifact : artifacts )
430 {
431
432 artifact.isSnapshot();
433
434 writer.startElement( "dependency" );
435 writer.startElement( "groupId" );
436 writer.writeText( artifact.getGroupId() );
437 writer.endElement();
438 writer.startElement( "artifactId" );
439 writer.writeText( artifact.getArtifactId() );
440 writer.endElement();
441 writer.startElement( "version" );
442 writer.writeText( artifact.getBaseVersion() );
443 if ( !StringUtils.isBlank( artifact.getClassifier() ) )
444 {
445 writer.startElement( "classifier" );
446 writer.writeText( artifact.getClassifier() );
447 writer.endElement();
448 }
449 writer.endElement();
450
451 if ( !Artifact.SCOPE_COMPILE.equals( artifact.getScope() ) )
452 {
453 writer.startElement( "scope" );
454 writer.writeText( artifact.getScope() );
455 writer.endElement();
456 }
457 writer.endElement();
458 }
459
460 getLog().info( System.lineSeparator() + out.getBuffer() );
461 }
462 }
463
464 private void writeScriptableOutput( Set<Artifact> artifacts )
465 {
466 if ( !artifacts.isEmpty() )
467 {
468 getLog().info( "Missing dependencies: " );
469 String pomFile = baseDir.getAbsolutePath() + File.separatorChar + "pom.xml";
470 StringBuilder buf = new StringBuilder();
471
472 for ( Artifact artifact : artifacts )
473 {
474
475 artifact.isSnapshot();
476
477
478 buf.append( scriptableFlag )
479 .append( ":" )
480 .append( pomFile )
481 .append( ":" )
482 .append( artifact.getDependencyConflictId() )
483 .append( ":" )
484 .append( artifact.getClassifier() )
485 .append( ":" )
486 .append( artifact.getBaseVersion() )
487 .append( ":" )
488 .append( artifact.getScope() )
489 .append( System.lineSeparator() );
490
491 }
492 getLog().info( System.lineSeparator() + buf );
493 }
494 }
495
496 private List<Artifact> filterDependencies( Set<Artifact> artifacts, String[] excludes )
497 throws MojoExecutionException
498 {
499 ArtifactFilter filter = new StrictPatternExcludesArtifactFilter( Arrays.asList( excludes ) );
500 List<Artifact> result = new ArrayList<>();
501
502 for ( Iterator<Artifact> it = artifacts.iterator(); it.hasNext(); )
503 {
504 Artifact artifact = it.next();
505 if ( !filter.include( artifact ) )
506 {
507 it.remove();
508 result.add( artifact );
509 }
510 }
511
512 return result;
513 }
514 }