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