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