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 }