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