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 }