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