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  
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.Comparator;
26  import java.util.List;
27  import java.util.Locale;
28  import java.util.Objects;
29  
30  import org.apache.maven.artifact.Artifact;
31  import org.apache.maven.artifact.versioning.VersionRange;
32  import org.apache.maven.doxia.sink.Sink;
33  import org.apache.maven.model.Plugin;
34  import org.apache.maven.model.ReportPlugin;
35  import org.apache.maven.plugin.logging.Log;
36  import org.apache.maven.plugins.annotations.Mojo;
37  import org.apache.maven.plugins.annotations.ResolutionScope;
38  import org.apache.maven.project.DefaultProjectBuildingRequest;
39  import org.apache.maven.project.MavenProject;
40  import org.apache.maven.project.ProjectBuilder;
41  import org.apache.maven.project.ProjectBuildingException;
42  import org.apache.maven.project.ProjectBuildingRequest;
43  import org.apache.maven.reporting.MavenReportException;
44  import org.apache.maven.repository.RepositorySystem;
45  import org.codehaus.plexus.i18n.I18N;
46  import org.codehaus.plexus.util.StringUtils;
47  
48  /**
49   * Generates the Project Plugins report.
50   *
51   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
52   * @since 2.1
53   */
54  @Mojo(name = "plugins", requiresDependencyResolution = ResolutionScope.TEST)
55  public class PluginsReport extends AbstractProjectInfoReport {
56  
57      @Inject
58      public PluginsReport(RepositorySystem repositorySystem, I18N i18n, ProjectBuilder projectBuilder) {
59          super(repositorySystem, i18n, projectBuilder);
60      }
61      // ----------------------------------------------------------------------
62      // Public methods
63      // ----------------------------------------------------------------------
64  
65      @Override
66      public boolean canGenerateReport() throws MavenReportException {
67          boolean result = super.canGenerateReport();
68          if (result && skipEmptyReport) {
69              result = !isEmpty(getProject().getBuildPlugins())
70                      || !isEmpty(getProject().getReportPlugins());
71          }
72  
73          return result;
74      }
75  
76      @Override
77      public void executeReport(Locale locale) {
78          PluginsRenderer r = new PluginsRenderer(
79                  getLog(),
80                  getSink(),
81                  locale,
82                  getI18N(locale),
83                  project.getBuildPlugins(),
84                  project.getReportPlugins(),
85                  project,
86                  projectBuilder,
87                  repositorySystem,
88                  getSession().getProjectBuildingRequest());
89          r.render();
90      }
91  
92      /** {@inheritDoc} */
93      public String getOutputName() {
94          return "plugins";
95      }
96  
97      @Override
98      protected String getI18Nsection() {
99          return "plugins";
100     }
101 
102     // ----------------------------------------------------------------------
103     // Private
104     // ----------------------------------------------------------------------
105 
106     /**
107      * Internal renderer class
108      */
109     protected static class PluginsRenderer extends AbstractProjectInfoRenderer {
110         private final Log log;
111 
112         private final List<Plugin> plugins;
113 
114         private final List<ReportPlugin> reports;
115 
116         private final MavenProject project;
117 
118         private final ProjectBuilder projectBuilder;
119 
120         private final RepositorySystem repositorySystem;
121 
122         private final ProjectBuildingRequest buildingRequest;
123 
124         /**
125          * @param log {@link #log}
126          * @param sink {@link Sink}
127          * @param locale {@link Locale}
128          * @param i18n {@link I18N}
129          * @param plugins {@link Artifact}
130          * @param reports {@link Artifact}
131          * @param project {@link MavenProject}
132          * @param projectBuilder {@link ProjectBuilder}
133          * @param repositorySystem {@link RepositorySystem}
134          * @param buildingRequest {@link ProjectBuildingRequest}
135          *
136          */
137         public PluginsRenderer(
138                 Log log,
139                 Sink sink,
140                 Locale locale,
141                 I18N i18n,
142                 List<Plugin> plugins,
143                 List<ReportPlugin> reports,
144                 MavenProject project,
145                 ProjectBuilder projectBuilder,
146                 RepositorySystem repositorySystem,
147                 ProjectBuildingRequest buildingRequest) {
148             super(sink, i18n, locale);
149 
150             this.log = log;
151 
152             this.plugins = new ArrayList<>(plugins);
153 
154             this.reports = new ArrayList<>(reports);
155 
156             this.project = project;
157 
158             this.projectBuilder = projectBuilder;
159 
160             this.repositorySystem = repositorySystem;
161 
162             this.buildingRequest = buildingRequest;
163         }
164 
165         @Override
166         protected String getI18Nsection() {
167             return "plugins";
168         }
169 
170         @Override
171         protected void renderBody() {
172             // === Section: Project Plugins.
173             renderSectionPlugins(true);
174 
175             // === Section: Project Reports.
176             renderSectionPlugins(false);
177         }
178 
179         /**
180          * @param isPlugins <code>true</code> to use <code>plugins</code> variable, <code>false</code> to use
181          * <code>reports</code> variable.
182          */
183         private void renderSectionPlugins(boolean isPlugins) {
184             List<GAV> list = isPlugins ? GAV.pluginsToGAV(plugins) : GAV.reportPluginsToGAV(reports, project);
185 
186             String[] tableHeader = getPluginTableHeader();
187 
188             startSection(getI18nString(isPlugins ? "build.title" : "report.title"));
189 
190             if (list.isEmpty()) {
191 
192                 paragraph(getI18nString(isPlugins ? "nolist" : "report.nolist"));
193                 endSection();
194                 return;
195             }
196 
197             Collections.sort(list, getPluginComparator());
198 
199             startTable();
200             tableHeader(tableHeader);
201 
202             ProjectBuildingRequest buildRequest = new DefaultProjectBuildingRequest(buildingRequest);
203             buildRequest.setRemoteRepositories(project.getPluginArtifactRepositories());
204             buildRequest.setProcessPlugins(false);
205 
206             for (GAV plugin : list) {
207                 VersionRange versionRange = VersionRange.createFromVersion(plugin.getVersion());
208 
209                 Artifact pluginArtifact = repositorySystem.createProjectArtifact(
210                         plugin.getGroupId(), plugin.getArtifactId(), versionRange.toString());
211                 try {
212                     MavenProject pluginProject =
213                             projectBuilder.build(pluginArtifact, buildRequest).getProject();
214 
215                     tableRow(getPluginRow(
216                             pluginProject.getGroupId(),
217                             pluginProject.getArtifactId(),
218                             pluginProject.getVersion(),
219                             pluginProject.getUrl()));
220                 } catch (ProjectBuildingException e) {
221                     log.info("Could not build project for " + plugin.getArtifactId(), e);
222                     tableRow(getPluginRow(plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion(), null));
223                 }
224             }
225             endTable();
226 
227             endSection();
228         }
229 
230         // ----------------------------------------------------------------------
231         // Private methods
232         // ----------------------------------------------------------------------
233 
234         private String[] getPluginTableHeader() {
235             // reused key...
236             String groupId = getI18nString("dependency-management", "column.groupId");
237             String artifactId = getI18nString("dependency-management", "column.artifactId");
238             String version = getI18nString("dependency-management", "column.version");
239             return new String[] {groupId, artifactId, version};
240         }
241 
242         private String[] getPluginRow(String groupId, String artifactId, String version, String link) {
243             artifactId = ProjectInfoReportUtils.getArtifactIdCell(artifactId, link);
244             return new String[] {groupId, artifactId, version};
245         }
246 
247         private static class GAV {
248             private final String groupId;
249             private final String artifactId;
250             private final String version;
251 
252             private GAV(Plugin plugin) {
253                 groupId = plugin.getGroupId();
254                 artifactId = plugin.getArtifactId();
255                 version = StringUtils.isEmpty(plugin.getVersion()) ? Artifact.RELEASE_VERSION : plugin.getVersion();
256             }
257 
258             private GAV(ReportPlugin reportPlugin, MavenProject project) {
259                 groupId = reportPlugin.getGroupId();
260                 artifactId = reportPlugin.getArtifactId();
261                 version = resolveReportPluginVersion(reportPlugin, project);
262             }
263 
264             public String getGroupId() {
265                 return groupId;
266             }
267 
268             public String getArtifactId() {
269                 return artifactId;
270             }
271 
272             public String getVersion() {
273                 return version;
274             }
275 
276             public static List<GAV> pluginsToGAV(List<Plugin> plugins) {
277                 List<GAV> result = new ArrayList<>(plugins.size());
278                 for (Plugin plugin : plugins) {
279                     result.add(new GAV(plugin));
280                 }
281                 return result;
282             }
283 
284             public static List<GAV> reportPluginsToGAV(List<ReportPlugin> reportPlugins, MavenProject project) {
285                 List<GAV> result = new ArrayList<>(reportPlugins.size());
286                 for (ReportPlugin reportPlugin : reportPlugins) {
287                     result.add(new GAV(reportPlugin, project));
288                 }
289                 return result;
290             }
291         }
292 
293         private Comparator<GAV> getPluginComparator() {
294             return new Comparator<GAV>() {
295                 /** {@inheritDoc} */
296                 public int compare(GAV a1, GAV a2) {
297                     int result = a1.groupId.compareTo(a2.groupId);
298                     if (result == 0) {
299                         result = a1.artifactId.compareTo(a2.artifactId);
300                     }
301                     return result;
302                 }
303             };
304         }
305 
306         /**
307          * Resolve report plugin version. Steps to find a plugin version stop after each step if a non <code>null</code>
308          * value has been found:
309          * <ol>
310          * <li>use the one defined in the reportPlugin configuration,</li>
311          * <li>search similar (same groupId and artifactId) mojo in the build/plugins section of the pom,</li>
312          * <li>search similar (same groupId and artifactId) mojo in the build/pluginManagement section of the pom,</li>
313          * <li>default value is RELEASE.</li>
314          * </ol>
315          *
316          * @param reportPlugin the report plugin to resolve the version
317          * @param project the current project
318          * @return the report plugin version
319          */
320         protected static String resolveReportPluginVersion(ReportPlugin reportPlugin, MavenProject project) {
321             // look for version defined in the reportPlugin configuration
322             if (reportPlugin.getVersion() != null) {
323                 return reportPlugin.getVersion();
324             }
325 
326             // search in the build section
327             if (project.getBuild() != null) {
328                 Plugin plugin = find(reportPlugin, project.getBuild().getPlugins());
329 
330                 if (plugin != null && plugin.getVersion() != null) {
331                     return plugin.getVersion();
332                 }
333             }
334 
335             // search in pluginManagement section
336             if (project.getBuild() != null && project.getBuild().getPluginManagement() != null) {
337                 Plugin plugin = find(
338                         reportPlugin, project.getBuild().getPluginManagement().getPlugins());
339 
340                 if (plugin != null && plugin.getVersion() != null) {
341                     return plugin.getVersion();
342                 }
343             }
344 
345             // empty version
346             return Artifact.RELEASE_VERSION;
347         }
348 
349         /**
350          * Search similar (same groupId and artifactId) plugin as a given report plugin.
351          *
352          * @param reportPlugin the report plugin to search for a similar plugin
353          * @param plugins the candidate plugins
354          * @return the first similar plugin
355          */
356         private static Plugin find(ReportPlugin reportPlugin, List<Plugin> plugins) {
357             if (plugins == null) {
358                 return null;
359             }
360             for (Plugin plugin : plugins) {
361                 if (Objects.equals(plugin.getArtifactId(), reportPlugin.getArtifactId())
362                         && Objects.equals(plugin.getGroupId(), reportPlugin.getGroupId())) {
363                     return plugin;
364                 }
365             }
366             return null;
367         }
368     }
369 }