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