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