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