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