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