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 @Parameter( defaultValue = "${project}", readonly = true, required = true )
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
141
142 @Parameter( property = "verbose", defaultValue = "false" )
143 private boolean verbose;
144
145
146
147
148
149
150
151
152 @Parameter( property = "tokens", defaultValue = "standard" )
153 private String tokens;
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 @Parameter( property = "includes" )
172 private String includes;
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 @Parameter( property = "excludes" )
191 private String excludes;
192
193
194
195
196 private DependencyNode rootNode;
197
198
199
200
201
202
203 @Parameter( property = "appendOutput", defaultValue = "false" )
204 private boolean appendOutput;
205
206
207
208
209
210
211 @Parameter( property = "skip", defaultValue = "false" )
212 private boolean skip;
213
214 @Parameter( defaultValue = "${localRepository}", readonly = true )
215 private ArtifactRepository localRepository;
216
217
218
219
220
221
222 public void execute()
223 throws MojoExecutionException, MojoFailureException
224 {
225 if ( isSkip() )
226 {
227 getLog().info( "Skipping plugin execution" );
228 return;
229 }
230
231 if ( output != null )
232 {
233 getLog().warn( "The parameter output is deprecated. Use outputFile instead." );
234 this.outputFile = output;
235 }
236
237 try
238 {
239 String dependencyTreeString;
240
241
242
243 ArtifactFilter artifactFilter = createResolvingArtifactFilter();
244
245 if ( verbose )
246 {
247
248 if ( ! isMaven2x() )
249 {
250 getLog().warn( "Using Maven 2 dependency tree to get verbose output, "
251 + "which may be inconsistent with actual Maven 3 resolution" );
252 }
253 dependencyTreeString =
254 serializeVerboseDependencyTree( dependencyTreeBuilder.buildDependencyTree( project,
255 localRepository,
256 artifactFilter ) );
257 }
258 else
259 {
260
261
262 rootNode = dependencyGraphBuilder.buildDependencyGraph( project, artifactFilter );
263
264 dependencyTreeString = serializeDependencyTree( rootNode );
265 }
266
267 if ( outputFile != null )
268 {
269 DependencyUtil.write( dependencyTreeString, outputFile, this.appendOutput, getLog() );
270
271 getLog().info( "Wrote dependency tree to: " + outputFile );
272 }
273 else
274 {
275 DependencyUtil.log( dependencyTreeString, getLog() );
276 }
277 }
278 catch ( DependencyGraphBuilderException exception )
279 {
280 throw new MojoExecutionException( "Cannot build project dependency graph", exception );
281 }
282 catch ( DependencyTreeBuilderException exception )
283 {
284 throw new MojoExecutionException( "Cannot build project dependency tree", exception );
285 }
286 catch ( IOException exception )
287 {
288 throw new MojoExecutionException( "Cannot serialise project dependency graph", exception );
289 }
290 }
291
292
293
294
295
296
297
298
299 public MavenProject getProject()
300 {
301 return project;
302 }
303
304
305
306
307
308
309 public DependencyNode getDependencyGraph()
310 {
311 return rootNode;
312 }
313
314 public boolean isSkip()
315 {
316 return skip;
317 }
318
319 public void setSkip( boolean skip )
320 {
321 this.skip = skip;
322 }
323
324
325
326
327
328
329
330
331 private ArtifactFilter createResolvingArtifactFilter()
332 {
333 ArtifactFilter filter;
334
335
336 if ( scope != null )
337 {
338 getLog().debug( "+ Resolving dependency tree for scope '" + scope + "'" );
339
340 filter = new ScopeArtifactFilter( scope );
341 }
342 else
343 {
344 filter = null;
345 }
346
347 return filter;
348 }
349
350
351
352
353
354
355
356 private String serializeDependencyTree( DependencyNode rootNode )
357 {
358 StringWriter writer = new StringWriter();
359
360 DependencyNodeVisitor visitor = getSerializingDependencyNodeVisitor( writer );
361
362
363 visitor = new BuildingDependencyNodeVisitor( visitor );
364
365 DependencyNodeFilter filter = createDependencyNodeFilter();
366
367 if ( filter != null )
368 {
369 CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
370 DependencyNodeVisitor firstPassVisitor = new FilteringDependencyNodeVisitor( collectingVisitor, filter );
371 rootNode.accept( firstPassVisitor );
372
373 DependencyNodeFilter secondPassFilter =
374 new AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
375 visitor = new FilteringDependencyNodeVisitor( visitor, secondPassFilter );
376 }
377
378 rootNode.accept( visitor );
379
380 return writer.toString();
381 }
382
383
384
385
386
387
388
389 private String serializeVerboseDependencyTree( org.apache.maven.shared.dependency.tree.DependencyNode rootNode )
390 {
391 StringWriter writer = new StringWriter();
392
393 final TreeTokens treeTokens = toTreeTokens( tokens );
394 org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor visitor =
395 new org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor( writer,
396 treeTokens );
397
398
399 visitor = new org.apache.maven.shared.dependency.tree.traversal.BuildingDependencyNodeVisitor( visitor );
400
401 org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter filter =
402 createVerboseDependencyNodeFilter();
403
404 if ( filter != null )
405 {
406 org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor collectingVisitor =
407 new org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor();
408 org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor firstPassVisitor =
409 new org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor( collectingVisitor,
410 filter );
411 rootNode.accept( firstPassVisitor );
412
413 final List<org.apache.maven.shared.dependency.tree.DependencyNode> nodes = collectingVisitor.getNodes();
414 org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter secondPass =
415 new org.apache.maven.shared.dependency.tree.filter.AncestorOrSelfDependencyNodeFilter( nodes );
416 visitor =
417 new org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor( visitor,
418 secondPass );
419 }
420
421 rootNode.accept( visitor );
422
423 return writer.toString();
424 }
425
426 public DependencyNodeVisitor getSerializingDependencyNodeVisitor( Writer writer )
427 {
428 if ( "graphml".equals( outputType ) )
429 {
430 return new GraphmlDependencyNodeVisitor( writer );
431 }
432 else if ( "tgf".equals( outputType ) )
433 {
434 return new TGFDependencyNodeVisitor( writer );
435 }
436 else if ( "dot".equals( outputType ) )
437 {
438 return new DOTDependencyNodeVisitor( writer );
439 }
440 else
441 {
442 return new SerializingDependencyNodeVisitor( writer, toGraphTokens( tokens ) );
443 }
444 }
445
446
447
448
449
450
451
452 private GraphTokens toGraphTokens( String tokens )
453 {
454 GraphTokens graphTokens;
455
456 if ( "whitespace".equals( tokens ) )
457 {
458 getLog().debug( "+ Using whitespace tree tokens" );
459
460 graphTokens = SerializingDependencyNodeVisitor.WHITESPACE_TOKENS;
461 }
462 else if ( "extended".equals( tokens ) )
463 {
464 getLog().debug( "+ Using extended tree tokens" );
465
466 graphTokens = SerializingDependencyNodeVisitor.EXTENDED_TOKENS;
467 }
468 else
469 {
470 graphTokens = SerializingDependencyNodeVisitor.STANDARD_TOKENS;
471 }
472
473 return graphTokens;
474 }
475
476
477
478
479
480
481
482 private TreeTokens toTreeTokens( String tokens )
483 {
484 TreeTokens treeTokens;
485
486 if ( "whitespace".equals( tokens ) )
487 {
488 getLog().debug( "+ Using whitespace tree tokens" );
489
490 treeTokens =
491 org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor.WHITESPACE_TOKENS;
492 }
493 else if ( "extended".equals( tokens ) )
494 {
495 getLog().debug( "+ Using extended tree tokens" );
496
497 treeTokens =
498 org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor.EXTENDED_TOKENS;
499 }
500 else
501 {
502 treeTokens =
503 org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor.STANDARD_TOKENS;
504 }
505
506 return treeTokens;
507 }
508
509
510
511
512
513
514 private DependencyNodeFilter createDependencyNodeFilter()
515 {
516 List<DependencyNodeFilter> filters = new ArrayList<DependencyNodeFilter>();
517
518
519 if ( includes != null )
520 {
521 List<String> patterns = Arrays.asList( includes.split( "," ) );
522
523 getLog().debug( "+ Filtering dependency tree by artifact include patterns: " + patterns );
524
525 ArtifactFilter artifactFilter = new StrictPatternIncludesArtifactFilter( patterns );
526 filters.add( new ArtifactDependencyNodeFilter( artifactFilter ) );
527 }
528
529
530 if ( excludes != null )
531 {
532 List<String> patterns = Arrays.asList( excludes.split( "," ) );
533
534 getLog().debug( "+ Filtering dependency tree by artifact exclude patterns: " + patterns );
535
536 ArtifactFilter artifactFilter = new StrictPatternExcludesArtifactFilter( patterns );
537 filters.add( new ArtifactDependencyNodeFilter( artifactFilter ) );
538 }
539
540 return filters.isEmpty() ? null : new AndDependencyNodeFilter( filters );
541 }
542
543
544
545
546
547
548 private org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter createVerboseDependencyNodeFilter()
549 {
550 List<org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter> filters =
551 new ArrayList<org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter>();
552 org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter filter;
553
554
555 if ( includes != null )
556 {
557 List<String> patterns = Arrays.asList( includes.split( "," ) );
558
559 getLog().debug( "+ Filtering dependency tree by artifact include patterns: " + patterns );
560
561 ArtifactFilter artifactFilter = new StrictPatternIncludesArtifactFilter( patterns );
562 filter = new org.apache.maven.shared.dependency.tree.filter.ArtifactDependencyNodeFilter( artifactFilter );
563 filters.add( filter );
564 }
565
566
567 if ( excludes != null )
568 {
569 List<String> patterns = Arrays.asList( excludes.split( "," ) );
570
571 getLog().debug( "+ Filtering dependency tree by artifact exclude patterns: " + patterns );
572
573 ArtifactFilter artifactFilter = new StrictPatternExcludesArtifactFilter( patterns );
574 filter = new org.apache.maven.shared.dependency.tree.filter.ArtifactDependencyNodeFilter( artifactFilter );
575 filters.add( filter );
576 }
577
578 return filters.isEmpty() ? null
579 : new org.apache.maven.shared.dependency.tree.filter.AndDependencyNodeFilter( filters );
580 }
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595 public static boolean containsVersion( VersionRange allowedRange, ArtifactVersion theVersion )
596 {
597 ArtifactVersion recommendedVersion = allowedRange.getRecommendedVersion();
598 if ( recommendedVersion == null )
599 {
600 @SuppressWarnings( "unchecked" )
601 List<Restriction> restrictions = allowedRange.getRestrictions();
602 for ( Restriction restriction : restrictions )
603 {
604 if ( restriction.containsVersion( theVersion ) )
605 {
606 return true;
607 }
608 }
609 }
610
611
612 return recommendedVersion.compareTo( theVersion ) <= 0;
613 }
614
615
616
617
618 protected static boolean isMaven2x()
619 {
620 return !canFindCoreClass( "org.apache.maven.project.DependencyResolutionRequest" );
621 }
622
623 private static boolean canFindCoreClass( String className )
624 {
625 try
626 {
627 Thread.currentThread().getContextClassLoader().loadClass( className );
628
629 return true;
630 }
631 catch ( ClassNotFoundException e )
632 {
633 return false;
634 }
635 }
636 }