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
179 @Parameter
180 private String[] ignoredDependencies = new String[0];
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 @Parameter
200 private String[] ignoredUsedUndeclaredDependencies = new String[0];
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 @Parameter
220 private String[] ignoredUnusedDeclaredDependencies = new String[0];
221
222
223
224
225
226
227 @Override
228 public void execute()
229 throws MojoExecutionException, MojoFailureException
230 {
231 if ( isSkip() )
232 {
233 getLog().info( "Skipping plugin execution" );
234 return;
235 }
236
237 if ( "pom".equals( project.getPackaging() ) )
238 {
239 getLog().info( "Skipping pom project" );
240 return;
241 }
242
243 if ( outputDirectory == null || !outputDirectory.exists() )
244 {
245 getLog().info( "Skipping project with no build directory" );
246 return;
247 }
248
249 boolean warning = checkDependencies();
250
251 if ( warning && failOnWarning )
252 {
253 throw new MojoExecutionException( "Dependency problems found" );
254 }
255 }
256
257
258
259
260
261 protected ProjectDependencyAnalyzer createProjectDependencyAnalyzer()
262 throws MojoExecutionException
263 {
264
265 final String role = ProjectDependencyAnalyzer.ROLE;
266 final String roleHint = analyzer;
267
268 try
269 {
270 final PlexusContainer container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
271
272 return (ProjectDependencyAnalyzer) container.lookup( role, roleHint );
273 }
274 catch ( Exception exception )
275 {
276 throw new MojoExecutionException( "Failed to instantiate ProjectDependencyAnalyser with role " + role
277 + " / role-hint " + roleHint, exception );
278 }
279 }
280
281 @Override
282 public void contextualize( Context theContext )
283 throws ContextException
284 {
285 this.context = theContext;
286 }
287
288
289
290
291 protected final boolean isSkip()
292 {
293 return skip;
294 }
295
296
297
298 private boolean checkDependencies()
299 throws MojoExecutionException
300 {
301 ProjectDependencyAnalysis analysis;
302 try
303 {
304 analysis = createProjectDependencyAnalyzer().analyze( project );
305
306 if ( usedDependencies != null )
307 {
308 analysis = analysis.forceDeclaredDependenciesUsage( usedDependencies );
309 }
310 }
311 catch ( ProjectDependencyAnalyzerException exception )
312 {
313 throw new MojoExecutionException( "Cannot analyze dependencies", exception );
314 }
315
316 if ( ignoreNonCompile )
317 {
318 analysis = analysis.ignoreNonCompile();
319 }
320
321 Set<Artifact> usedDeclared = new LinkedHashSet<Artifact>( analysis.getUsedDeclaredArtifacts() );
322 Set<Artifact> usedUndeclared = new LinkedHashSet<Artifact>( analysis.getUsedUndeclaredArtifacts() );
323 Set<Artifact> unusedDeclared = new LinkedHashSet<Artifact>( analysis.getUnusedDeclaredArtifacts() );
324
325 Set<Artifact> ignoredUsedUndeclared = new LinkedHashSet<Artifact>();
326 Set<Artifact> ignoredUnusedDeclared = new LinkedHashSet<Artifact>();
327
328 ignoredUsedUndeclared.addAll( filterDependencies( usedUndeclared, ignoredDependencies ) );
329 ignoredUsedUndeclared.addAll( filterDependencies( usedUndeclared, ignoredUsedUndeclaredDependencies ) );
330
331 ignoredUnusedDeclared.addAll( filterDependencies( unusedDeclared, ignoredDependencies ) );
332 ignoredUnusedDeclared.addAll( filterDependencies( unusedDeclared, ignoredUnusedDeclaredDependencies ) );
333
334 boolean reported = false;
335 boolean warning = false;
336
337 if ( verbose && !usedDeclared.isEmpty() )
338 {
339 getLog().info( "Used declared dependencies found:" );
340
341 logArtifacts( analysis.getUsedDeclaredArtifacts(), false );
342 reported = true;
343 }
344
345 if ( !usedUndeclared.isEmpty() )
346 {
347 getLog().warn( "Used undeclared dependencies found:" );
348
349 logArtifacts( usedUndeclared, true );
350 reported = true;
351 warning = true;
352 }
353
354 if ( !unusedDeclared.isEmpty() )
355 {
356 getLog().warn( "Unused declared dependencies found:" );
357
358 logArtifacts( unusedDeclared, true );
359 reported = true;
360 warning = true;
361 }
362
363 if ( verbose && !ignoredUsedUndeclared.isEmpty() )
364 {
365 getLog().info( "Ignored used undeclared dependencies:" );
366
367 logArtifacts( ignoredUsedUndeclared, false );
368 reported = true;
369 }
370
371 if ( verbose && !ignoredUnusedDeclared.isEmpty() )
372 {
373 getLog().info( "Ignored unused declared dependencies:" );
374
375 logArtifacts( ignoredUnusedDeclared, false );
376 reported = true;
377 }
378
379 if ( outputXML )
380 {
381 writeDependencyXML( usedUndeclared );
382 }
383
384 if ( scriptableOutput )
385 {
386 writeScriptableOutput( usedUndeclared );
387 }
388
389 if ( !reported )
390 {
391 getLog().info( "No dependency problems found" );
392 }
393
394 return warning;
395 }
396
397 private void logArtifacts( Set<Artifact> artifacts, boolean warn )
398 {
399 if ( artifacts.isEmpty() )
400 {
401 getLog().info( " None" );
402 }
403 else
404 {
405 for ( Artifact artifact : artifacts )
406 {
407
408 artifact.isSnapshot();
409
410 if ( warn )
411 {
412 getLog().warn( " " + artifact );
413 }
414 else
415 {
416 getLog().info( " " + artifact );
417 }
418
419 }
420 }
421 }
422
423 private void writeDependencyXML( Set<Artifact> artifacts )
424 {
425 if ( !artifacts.isEmpty() )
426 {
427 getLog().info( "Add the following to your pom to correct the missing dependencies: " );
428
429 StringWriter out = new StringWriter();
430 PrettyPrintXMLWriter writer = new PrettyPrintXMLWriter( out );
431
432 for ( Artifact artifact : artifacts )
433 {
434
435 artifact.isSnapshot();
436
437 writer.startElement( "dependency" );
438 writer.startElement( "groupId" );
439 writer.writeText( artifact.getGroupId() );
440 writer.endElement();
441 writer.startElement( "artifactId" );
442 writer.writeText( artifact.getArtifactId() );
443 writer.endElement();
444 writer.startElement( "version" );
445 writer.writeText( artifact.getBaseVersion() );
446 if ( !StringUtils.isBlank( artifact.getClassifier() ) )
447 {
448 writer.startElement( "classifier" );
449 writer.writeText( artifact.getClassifier() );
450 writer.endElement();
451 }
452 writer.endElement();
453
454 if ( !Artifact.SCOPE_COMPILE.equals( artifact.getScope() ) )
455 {
456 writer.startElement( "scope" );
457 writer.writeText( artifact.getScope() );
458 writer.endElement();
459 }
460 writer.endElement();
461 }
462
463 getLog().info( "\n" + out.getBuffer() );
464 }
465 }
466
467 private void writeScriptableOutput( Set<Artifact> artifacts )
468 {
469 if ( !artifacts.isEmpty() )
470 {
471 getLog().info( "Missing dependencies: " );
472 String pomFile = baseDir.getAbsolutePath() + File.separatorChar + "pom.xml";
473 StringBuilder buf = new StringBuilder();
474
475 for ( Artifact artifact : artifacts )
476 {
477
478 artifact.isSnapshot();
479
480
481 buf.append( scriptableFlag )
482 .append( ":" )
483 .append( pomFile )
484 .append( ":" )
485 .append( artifact.getDependencyConflictId() )
486 .append( ":" )
487 .append( artifact.getClassifier() )
488 .append( ":" )
489 .append( artifact.getBaseVersion() )
490 .append( ":" )
491 .append( artifact.getScope() )
492 .append( "\n" );
493
494 }
495 getLog().info( "\n" + buf );
496 }
497 }
498
499 private List<Artifact> filterDependencies( Set<Artifact> artifacts, String[] excludes )
500 throws MojoExecutionException
501 {
502 ArtifactFilter filter = new StrictPatternExcludesArtifactFilter( Arrays.asList( excludes ) );
503 List<Artifact> result = new ArrayList<Artifact>();
504
505 for ( Iterator<Artifact> it = artifacts.iterator(); it.hasNext(); )
506 {
507 Artifact artifact = it.next();
508 if ( !filter.include( artifact ) )
509 {
510 it.remove();
511 result.add( artifact );
512 }
513 }
514
515 return result;
516 }
517 }