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