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