1 package org.apache.maven.plugin.dependency.tree;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.artifact.repository.ArtifactRepository;
23 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
24 import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
25 import org.apache.maven.artifact.versioning.ArtifactVersion;
26 import org.apache.maven.artifact.versioning.Restriction;
27 import org.apache.maven.artifact.versioning.VersionRange;
28 import org.apache.maven.plugin.AbstractMojo;
29 import org.apache.maven.plugin.MojoExecutionException;
30 import org.apache.maven.plugin.MojoFailureException;
31 import org.apache.maven.plugin.dependency.utils.DependencyUtil;
32 import org.apache.maven.plugins.annotations.Component;
33 import org.apache.maven.plugins.annotations.Mojo;
34 import org.apache.maven.plugins.annotations.Parameter;
35 import org.apache.maven.plugins.annotations.ResolutionScope;
36 import org.apache.maven.project.MavenProject;
37 import org.apache.maven.shared.artifact.filter.StrictPatternExcludesArtifactFilter;
38 import org.apache.maven.shared.artifact.filter.StrictPatternIncludesArtifactFilter;
39 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
40 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
41 import org.apache.maven.shared.dependency.graph.DependencyNode;
42 import org.apache.maven.shared.dependency.graph.filter.AncestorOrSelfDependencyNodeFilter;
43 import org.apache.maven.shared.dependency.graph.filter.AndDependencyNodeFilter;
44 import org.apache.maven.shared.dependency.graph.filter.ArtifactDependencyNodeFilter;
45 import org.apache.maven.shared.dependency.graph.filter.DependencyNodeFilter;
46 import org.apache.maven.shared.dependency.graph.traversal.BuildingDependencyNodeVisitor;
47 import org.apache.maven.shared.dependency.graph.traversal.CollectingDependencyNodeVisitor;
48 import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor;
49 import org.apache.maven.shared.dependency.graph.traversal.FilteringDependencyNodeVisitor;
50 import org.apache.maven.shared.dependency.graph.traversal.SerializingDependencyNodeVisitor;
51 import org.apache.maven.shared.dependency.graph.traversal.SerializingDependencyNodeVisitor.GraphTokens;
52 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
53 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
54 import org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor.TreeTokens;
55
56 import java.io.File;
57 import java.io.IOException;
58 import java.io.StringWriter;
59 import java.io.Writer;
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.List;
63
64
65
66
67
68
69
70
71 @Mojo( name = "tree", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true )
72 public class TreeMojo
73 extends AbstractMojo
74 {
75
76
77
78
79
80 @Component
81 private MavenProject project;
82
83
84
85
86 @Component( hint = "default" )
87 private DependencyGraphBuilder dependencyGraphBuilder;
88
89
90
91
92 @Component
93 private DependencyTreeBuilder dependencyTreeBuilder;
94
95
96
97
98
99
100
101 @Parameter( property = "output" )
102 private File output;
103
104
105
106
107
108
109
110 @Parameter( property = "outputFile" )
111 private File outputFile;
112
113
114
115
116
117
118
119
120
121
122 @Parameter( property = "outputType", defaultValue = "text" )
123 private String outputType;
124
125
126
127
128
129
130
131
132 @Parameter( property = "scope" )
133 private String scope;
134
135
136
137
138
139
140 @Parameter( property = "verbose", defaultValue = "false" )
141 private boolean verbose;
142
143
144
145
146
147
148
149
150 @Parameter( property = "tokens", defaultValue = "standard" )
151 private String tokens;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 @Parameter( property = "includes" )
170 private String includes;
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188 @Parameter( property = "excludes" )
189 private String excludes;
190
191
192
193
194 private DependencyNode rootNode;
195
196
197
198
199
200
201 @Parameter( property = "appendOutput", defaultValue = "false" )
202 private boolean appendOutput;
203
204
205
206
207
208
209 @Parameter( property = "skip", defaultValue = "false" )
210 private boolean skip;
211
212 @Parameter( defaultValue = "${localRepository}", readonly = true )
213 private ArtifactRepository localRepository;
214
215
216
217
218
219
220 public void execute()
221 throws MojoExecutionException, MojoFailureException
222 {
223 if ( isSkip() )
224 {
225 getLog().info( "Skipping plugin execution" );
226 return;
227 }
228
229 if ( output != null )
230 {
231 getLog().warn( "The parameter output is deprecated. Use outputFile instead." );
232 this.outputFile = output;
233 }
234
235 try
236 {
237 String dependencyTreeString = null;
238
239
240
241 ArtifactFilter artifactFilter = createResolvingArtifactFilter();
242
243 if ( verbose )
244 {
245
246 dependencyTreeString =
247 serializeVerboseDependencyTree( dependencyTreeBuilder.buildDependencyTree( project,
248 localRepository,
249 artifactFilter ) );
250 }
251 else
252 {
253
254
255 rootNode = dependencyGraphBuilder.buildDependencyGraph( project, artifactFilter );
256
257 dependencyTreeString = serializeDependencyTree( rootNode );
258 }
259
260 if ( outputFile != null )
261 {
262 DependencyUtil.write( dependencyTreeString, outputFile, this.appendOutput, getLog() );
263
264 getLog().info( "Wrote dependency tree to: " + outputFile );
265 }
266 else
267 {
268 DependencyUtil.log( dependencyTreeString, getLog() );
269 }
270 }
271 catch ( DependencyGraphBuilderException exception )
272 {
273 throw new MojoExecutionException( "Cannot build project dependency graph", exception );
274 }
275 catch ( DependencyTreeBuilderException exception )
276 {
277 throw new MojoExecutionException( "Cannot build project dependency tree", exception );
278 }
279 catch ( IOException exception )
280 {
281 throw new MojoExecutionException( "Cannot serialise project dependency graph", exception );
282 }
283 }
284
285
286
287
288
289
290
291
292 public MavenProject getProject()
293 {
294 return project;
295 }
296
297
298
299
300
301
302 public DependencyNode getDependencyGraph()
303 {
304 return rootNode;
305 }
306
307 public boolean isSkip()
308 {
309 return skip;
310 }
311
312 public void setSkip( boolean skip )
313 {
314 this.skip = skip;
315 }
316
317
318
319
320
321
322
323
324 private ArtifactFilter createResolvingArtifactFilter()
325 {
326 ArtifactFilter filter;
327
328
329 if ( scope != null )
330 {
331 getLog().debug( "+ Resolving dependency tree for scope '" + scope + "'" );
332
333 filter = new ScopeArtifactFilter( scope );
334 }
335 else
336 {
337 filter = null;
338 }
339
340 return filter;
341 }
342
343
344
345
346
347
348
349 private String serializeDependencyTree( DependencyNode rootNode )
350 {
351 StringWriter writer = new StringWriter();
352
353 DependencyNodeVisitor visitor = getSerializingDependencyNodeVisitor( writer );
354
355
356 visitor = new BuildingDependencyNodeVisitor( visitor );
357
358 DependencyNodeFilter filter = createDependencyNodeFilter();
359
360 if ( filter != null )
361 {
362 CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
363 DependencyNodeVisitor firstPassVisitor = new FilteringDependencyNodeVisitor( collectingVisitor, filter );
364 rootNode.accept( firstPassVisitor );
365
366 DependencyNodeFilter secondPassFilter =
367 new AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
368 visitor = new FilteringDependencyNodeVisitor( visitor, secondPassFilter );
369 }
370
371 rootNode.accept( visitor );
372
373 return writer.toString();
374 }
375
376
377
378
379
380
381
382 private String serializeVerboseDependencyTree( org.apache.maven.shared.dependency.tree.DependencyNode rootNode )
383 {
384 StringWriter writer = new StringWriter();
385
386 org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor visitor =
387 new org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor( writer,
388 toTreeTokens( tokens ) );
389
390
391 visitor = new org.apache.maven.shared.dependency.tree.traversal.BuildingDependencyNodeVisitor( visitor );
392
393 org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter filter = createVerboseDependencyNodeFilter();
394
395 if ( filter != null )
396 {
397 org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor collectingVisitor =
398 new org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor();
399 org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor firstPassVisitor =
400 new org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor( collectingVisitor,
401 filter );
402 rootNode.accept( firstPassVisitor );
403
404 org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter secondPassFilter =
405 new org.apache.maven.shared.dependency.tree.filter.AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
406 visitor =
407 new org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor( visitor,
408 secondPassFilter );
409 }
410
411 rootNode.accept( visitor );
412
413 return writer.toString();
414 }
415
416 public DependencyNodeVisitor getSerializingDependencyNodeVisitor( Writer writer )
417 {
418 if ( "graphml".equals( outputType ) )
419 {
420 return new GraphmlDependencyNodeVisitor( writer );
421 }
422 else if ( "tgf".equals( outputType ) )
423 {
424 return new TGFDependencyNodeVisitor( writer );
425 }
426 else if ( "dot".equals( outputType ) )
427 {
428 return new DOTDependencyNodeVisitor( writer );
429 }
430 else
431 {
432 return new SerializingDependencyNodeVisitor( writer, toGraphTokens( tokens ) );
433 }
434 }
435
436
437
438
439
440
441
442 private GraphTokens toGraphTokens( String tokens )
443 {
444 GraphTokens graphTokens;
445
446 if ( "whitespace".equals( tokens ) )
447 {
448 getLog().debug( "+ Using whitespace tree tokens" );
449
450 graphTokens = SerializingDependencyNodeVisitor.WHITESPACE_TOKENS;
451 }
452 else if ( "extended".equals( tokens ) )
453 {
454 getLog().debug( "+ Using extended tree tokens" );
455
456 graphTokens = SerializingDependencyNodeVisitor.EXTENDED_TOKENS;
457 }
458 else
459 {
460 graphTokens = SerializingDependencyNodeVisitor.STANDARD_TOKENS;
461 }
462
463 return graphTokens;
464 }
465
466
467
468
469
470
471
472 private TreeTokens toTreeTokens( String tokens )
473 {
474 TreeTokens treeTokens;
475
476 if ( "whitespace".equals( tokens ) )
477 {
478 getLog().debug( "+ Using whitespace tree tokens" );
479
480 treeTokens =
481 org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor.WHITESPACE_TOKENS;
482 }
483 else if ( "extended".equals( tokens ) )
484 {
485 getLog().debug( "+ Using extended tree tokens" );
486
487 treeTokens =
488 org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor.EXTENDED_TOKENS;
489 }
490 else
491 {
492 treeTokens =
493 org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor.STANDARD_TOKENS;
494 }
495
496 return treeTokens;
497 }
498
499
500
501
502
503
504 private DependencyNodeFilter createDependencyNodeFilter()
505 {
506 List<DependencyNodeFilter> filters = new ArrayList<DependencyNodeFilter>();
507
508
509 if ( includes != null )
510 {
511 List<String> patterns = Arrays.asList( includes.split( "," ) );
512
513 getLog().debug( "+ Filtering dependency tree by artifact include patterns: " + patterns );
514
515 ArtifactFilter artifactFilter = new StrictPatternIncludesArtifactFilter( patterns );
516 filters.add( new ArtifactDependencyNodeFilter( artifactFilter ) );
517 }
518
519
520 if ( excludes != null )
521 {
522 List<String> patterns = Arrays.asList( excludes.split( "," ) );
523
524 getLog().debug( "+ Filtering dependency tree by artifact exclude patterns: " + patterns );
525
526 ArtifactFilter artifactFilter = new StrictPatternExcludesArtifactFilter( patterns );
527 filters.add( new ArtifactDependencyNodeFilter( artifactFilter ) );
528 }
529
530 return filters.isEmpty() ? null : new AndDependencyNodeFilter( filters );
531 }
532
533
534
535
536
537
538 private org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter createVerboseDependencyNodeFilter()
539 {
540 List<org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter> filters =
541 new ArrayList<org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter>();
542
543
544 if ( includes != null )
545 {
546 List<String> patterns = Arrays.asList( includes.split( "," ) );
547
548 getLog().debug( "+ Filtering dependency tree by artifact include patterns: " + patterns );
549
550 ArtifactFilter artifactFilter = new StrictPatternIncludesArtifactFilter( patterns );
551 filters.add( new org.apache.maven.shared.dependency.tree.filter.ArtifactDependencyNodeFilter(
552 artifactFilter ) );
553 }
554
555
556 if ( excludes != null )
557 {
558 List<String> patterns = Arrays.asList( excludes.split( "," ) );
559
560 getLog().debug( "+ Filtering dependency tree by artifact exclude patterns: " + patterns );
561
562 ArtifactFilter artifactFilter = new StrictPatternExcludesArtifactFilter( patterns );
563 filters.add( new org.apache.maven.shared.dependency.tree.filter.ArtifactDependencyNodeFilter(
564 artifactFilter ) );
565 }
566
567 return filters.isEmpty() ? null
568 : new org.apache.maven.shared.dependency.tree.filter.AndDependencyNodeFilter( filters );
569 }
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584 public static boolean containsVersion( VersionRange allowedRange, ArtifactVersion theVersion )
585 {
586 ArtifactVersion recommendedVersion = allowedRange.getRecommendedVersion();
587 if ( recommendedVersion == null )
588 {
589 @SuppressWarnings( "unchecked" )
590 List<Restriction> restrictions = allowedRange.getRestrictions();
591 for ( Restriction restriction : restrictions )
592 {
593 if ( restriction.containsVersion( theVersion ) )
594 {
595 return true;
596 }
597 }
598 }
599
600
601 return recommendedVersion.compareTo( theVersion ) <= 0;
602 }
603
604 }