View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.report.projectinfo;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  
24  import java.io.BufferedReader;
25  import java.io.File;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.InputStreamReader;
30  import java.io.LineNumberReader;
31  import java.io.OutputStream;
32  import java.nio.charset.StandardCharsets;
33  import java.util.Locale;
34  
35  import org.apache.maven.artifact.Artifact;
36  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
37  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
38  import org.apache.maven.plugins.annotations.Mojo;
39  import org.apache.maven.plugins.annotations.Parameter;
40  import org.apache.maven.plugins.annotations.ResolutionScope;
41  import org.apache.maven.project.DefaultProjectBuildingRequest;
42  import org.apache.maven.project.ProjectBuilder;
43  import org.apache.maven.project.ProjectBuildingRequest;
44  import org.apache.maven.report.projectinfo.dependencies.Dependencies;
45  import org.apache.maven.report.projectinfo.dependencies.DependenciesReportConfiguration;
46  import org.apache.maven.report.projectinfo.dependencies.RepositoryUtils;
47  import org.apache.maven.report.projectinfo.dependencies.renderer.DependenciesRenderer;
48  import org.apache.maven.reporting.MavenReportException;
49  import org.apache.maven.repository.RepositorySystem;
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.jar.classes.JarClassesAnalysis;
54  import org.codehaus.plexus.i18n.I18N;
55  import org.codehaus.plexus.util.IOUtil;
56  
57  /**
58   * Generates the Project Dependencies report.
59   *
60   * @author <a href="mailto:jason@maven.org">Jason van Zyl </a>
61   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton </a>
62   * @since 2.0
63   */
64  @Mojo(name = "dependencies", requiresDependencyResolution = ResolutionScope.TEST)
65  public class DependenciesReport extends AbstractProjectInfoReport {
66      /**
67       * Images resources dir
68       */
69      private static final String RESOURCES_DIR = "org/apache/maven/report/projectinfo/resources";
70  
71      // ----------------------------------------------------------------------
72      // Mojo parameters
73      // ----------------------------------------------------------------------
74  
75      /**
76       * Display file details for each dependency, such as: file size, number of
77       * classes, number of packages etc.
78       *
79       * @since 2.1
80       */
81      @Parameter(property = "dependency.details.enabled", defaultValue = "true")
82      private boolean dependencyDetailsEnabled;
83  
84      // ----------------------------------------------------------------------
85      // Mojo components
86      // ----------------------------------------------------------------------
87  
88      /**
89       * Dependency graph builder component.
90       *
91       * @since 2.5
92       */
93      private final DependencyGraphBuilder dependencyGraphBuilder;
94  
95      /**
96       * Jar classes analyzer component.
97       *
98       * @since 2.1
99       */
100     private final JarClassesAnalysis classesAnalyzer;
101 
102     private final RepositoryUtils repoUtils;
103 
104     @Inject
105     protected DependenciesReport(
106             RepositorySystem repositorySystem,
107             I18N i18n,
108             ProjectBuilder projectBuilder,
109             @Named("default") DependencyGraphBuilder dependencyGraphBuilder,
110             JarClassesAnalysis classesAnalyzer,
111             RepositoryUtils repoUtils) {
112         super(repositorySystem, i18n, projectBuilder);
113         this.dependencyGraphBuilder = dependencyGraphBuilder;
114         this.classesAnalyzer = classesAnalyzer;
115         this.repoUtils = repoUtils;
116     }
117 
118     // ----------------------------------------------------------------------
119     // Public methods
120     // ----------------------------------------------------------------------
121 
122     @Override
123     public boolean canGenerateReport() throws MavenReportException {
124         boolean result = super.canGenerateReport();
125         if (result && skipEmptyReport) {
126             // This seems to be a bit too much but the DependenciesRenderer applies the same logic
127             DependencyNode dependencyNode = resolveProject();
128             Dependencies dependencies = new Dependencies(project, dependencyNode, classesAnalyzer);
129             result = dependencies.hasDependencies();
130         }
131 
132         return result;
133     }
134 
135     @Override
136     public void executeReport(Locale locale) {
137         try {
138             copyResources(getReportOutputDirectory());
139         } catch (IOException e) {
140             getLog().error("Cannot copy resources", e);
141         }
142 
143         DependencyNode dependencyNode = resolveProject();
144 
145         Dependencies dependencies = new Dependencies(project, dependencyNode, classesAnalyzer);
146 
147         DependenciesReportConfiguration config = new DependenciesReportConfiguration(dependencyDetailsEnabled);
148 
149         DependenciesRenderer r = new DependenciesRenderer(
150                 getSink(),
151                 locale,
152                 getI18N(locale),
153                 getLog(),
154                 dependencies,
155                 dependencyNode,
156                 config,
157                 repoUtils,
158                 getLicenseMappings());
159         r.render();
160     }
161 
162     /**
163      * {@inheritDoc}
164      */
165     public String getOutputName() {
166         return "dependencies";
167     }
168 
169     @Override
170     protected String getI18Nsection() {
171         return "dependencies";
172     }
173 
174     // ----------------------------------------------------------------------
175     // Private methods
176     // ----------------------------------------------------------------------
177 
178     /**
179      * @return resolve the dependency tree
180      */
181     private DependencyNode resolveProject() {
182         try {
183             ArtifactFilter artifactFilter = new ScopeArtifactFilter(Artifact.SCOPE_TEST);
184             ProjectBuildingRequest buildingRequest =
185                     new DefaultProjectBuildingRequest(getSession().getProjectBuildingRequest());
186             buildingRequest.setProject(project);
187             return dependencyGraphBuilder.buildDependencyGraph(buildingRequest, artifactFilter);
188         } catch (DependencyGraphBuilderException e) {
189             getLog().error("Unable to build dependency tree.", e);
190             return null;
191         }
192     }
193 
194     /**
195      * @param outputDirectory the wanted output directory
196      * @throws IOException if any
197      */
198     private void copyResources(File outputDirectory) throws IOException {
199         InputStream resourceList = getClass().getClassLoader().getResourceAsStream(RESOURCES_DIR + "/resources.txt");
200         if (resourceList != null) {
201             try (BufferedReader reader =
202                     new LineNumberReader(new InputStreamReader(resourceList, StandardCharsets.US_ASCII))) {
203                 for (String line = reader.readLine(); line != null; line = reader.readLine()) {
204                     try (InputStream in = getClass().getClassLoader().getResourceAsStream(RESOURCES_DIR + "/" + line)) {
205                         if (in == null) {
206                             throw new IOException("The resource " + line + " doesn't exist.");
207                         }
208 
209                         File outputFile = new File(outputDirectory, line);
210                         if (!outputFile.getParentFile().exists()) {
211                             outputFile.getParentFile().mkdirs();
212                         }
213 
214                         try (OutputStream out = new FileOutputStream(outputFile)) {
215                             IOUtil.copy(in, out);
216                         }
217                     }
218                 }
219             }
220         }
221     }
222 }