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