View Javadoc
1   package org.apache.maven.reporting.exec;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Set;
28  
29  import org.apache.maven.lifecycle.LifecycleExecutor;
30  import org.apache.maven.model.Plugin;
31  import org.apache.maven.plugin.MavenPluginManager;
32  import org.apache.maven.plugin.Mojo;
33  import org.apache.maven.plugin.MojoExecution;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugin.MojoNotFoundException;
36  import org.apache.maven.plugin.PluginConfigurationException;
37  import org.apache.maven.plugin.PluginContainerException;
38  import org.apache.maven.plugin.descriptor.MojoDescriptor;
39  import org.apache.maven.plugin.descriptor.PluginDescriptor;
40  import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
41  import org.apache.maven.plugin.version.PluginVersionRequest;
42  import org.apache.maven.plugin.version.PluginVersionResolutionException;
43  import org.apache.maven.plugin.version.PluginVersionResolver;
44  import org.apache.maven.plugin.version.PluginVersionResult;
45  import org.apache.maven.project.MavenProject;
46  import org.apache.maven.reporting.MavenReport;
47  import org.codehaus.plexus.component.annotations.Component;
48  import org.codehaus.plexus.component.annotations.Requirement;
49  import org.codehaus.plexus.configuration.PlexusConfiguration;
50  import org.codehaus.plexus.logging.Logger;
51  import org.apache.maven.shared.utils.StringUtils;
52  import org.codehaus.plexus.util.xml.Xpp3Dom;
53  import org.codehaus.plexus.util.xml.Xpp3DomUtils;
54  
55  /**
56   * <p>
57   * This component will build some {@link MavenReportExecution} from {@link MavenReportExecutorRequest}. If a
58   * {@link MavenReport} needs to fork a lifecycle, this fork is executed here. It will ask the core to get some
59   * informations in order to correctly setup {@link MavenReport}.
60   * </p>
61   * <p>
62   * <b>Note</b> if no version is defined in the report plugin, the version will be searched with
63   * {@link #resolvePluginVersion(ReportPlugin, MavenReportExecutorRequest) resolvePluginVersion(...)} method:
64   * <ol>
65   * <li>use the one defined in the reportPlugin configuration,</li>
66   * <li>search similar (same groupId and artifactId) plugin in the build/plugins section of the pom,</li>
67   * <li>search similar (same groupId and artifactId) plugin in the build/pluginManagement section of the pom,</li>
68   * <li>ask {@link PluginVersionResolver} to get a fallback version (display a warning as it's not a recommended use).
69   * </li>
70   * </ol>
71   * </p>
72   * <p>
73   * Following steps are done:
74   * <ul>
75   * <li>get {@link PluginDescriptor} from the {@link MavenPluginManager} (through
76   * {@link MavenPluginManagerHelper#getPluginDescriptor(Plugin, org.apache.maven.execution.MavenSession)
77   * MavenPluginManagerHelper.getPluginDescriptor(...)} to protect from core API change)</li>
78   * <li>setup a {@link ClassLoader}, with the Site plugin classloader as parent for the report execution. <br>
79   * Notice that some classes are imported from the current Site plugin ClassRealm: see {@link #IMPORTS}. Corresponding
80   * artifacts are excluded from the artifact resolution: <code>doxia-site-renderer</code>, <code>doxia-sink-api</code>
81   *  and <code>maven-reporting-api</code>.<br>
82   * Work is done using {@link MavenPluginManager} (through
83   * {@link MavenPluginManagerHelper#setupPluginRealm(PluginDescriptor, MavenSession, ClassLoader, List, List)
84   * MavenPluginManagerHelper.setupPluginRealm(...)} to protect from core API change)</li>
85   * <li>setup the mojo using {@link MavenPluginManager#getConfiguredMojo(Class, MavenSession, MojoExecution)
86   * MavenPluginManager.getConfiguredMojo(...)}</li>
87   * <li>verify with {@link LifecycleExecutor#calculateForkedExecutions(MojoExecution, MavenSession)
88   * LifecycleExecutor.calculateForkedExecutions(...)} if any forked execution is needed: if yes, execute the forked
89   * execution here</li>
90   * </ul>
91   * </p>
92   * 
93   * @author Olivier Lamy
94   */
95  @Component( role = MavenReportExecutor.class )
96  public class DefaultMavenReportExecutor
97      implements MavenReportExecutor
98  {
99      @Requirement
100     private Logger logger;
101 
102     @Requirement
103     protected MavenPluginManager mavenPluginManager;
104 
105     @Requirement
106     protected MavenPluginManagerHelper mavenPluginManagerHelper;
107 
108     @Requirement
109     protected LifecycleExecutor lifecycleExecutor;
110 
111     @Requirement
112     protected PluginVersionResolver pluginVersionResolver;
113 
114     private static final List<String> IMPORTS = Arrays.asList( "org.apache.maven.reporting.MavenReport",
115                                                                "org.apache.maven.reporting.MavenMultiPageReport",
116                                                                "org.apache.maven.doxia.siterenderer.Renderer",
117                                                                "org.apache.maven.doxia.sink.SinkFactory",
118                                                                "org.codehaus.doxia.sink.Sink",
119                                                                "org.apache.maven.doxia.sink.Sink",
120                                                                "org.apache.maven.doxia.sink.SinkEventAttributes",
121                                                                "org.apache.maven.doxia.logging.LogEnabled",
122                                                                "org.apache.maven.doxia.logging.Log" );
123 
124     private static final List<String> EXCLUDES = Arrays.asList( "doxia-site-renderer", "doxia-sink-api",
125                                                                 "maven-reporting-api" );
126 
127     public List<MavenReportExecution> buildMavenReports( MavenReportExecutorRequest mavenReportExecutorRequest )
128         throws MojoExecutionException
129     {
130         if ( mavenReportExecutorRequest.getReportPlugins() == null )
131         {
132             return Collections.emptyList();
133         }
134         getLog().debug( "DefaultMavenReportExecutor.buildMavenReports()" );
135 
136         Set<String> reportPluginKeys = new HashSet<String>();
137         List<MavenReportExecution> reportExecutions = new ArrayList<MavenReportExecution>();
138 
139         String pluginKey = "";
140         try
141         {
142             for ( ReportPlugin reportPlugin : mavenReportExecutorRequest.getReportPlugins() )
143             {
144                 pluginKey = reportPlugin.getGroupId() + ':' + reportPlugin.getArtifactId();
145 
146                 if ( !reportPluginKeys.add( pluginKey ) )
147                 {
148                     logger.info( "plugin " + pluginKey + " will be executed more than one time" );
149                 }
150 
151                 reportExecutions.addAll( buildReportPlugin( mavenReportExecutorRequest, reportPlugin ) );
152             }
153         }
154         catch ( Exception e )
155         {
156             throw new MojoExecutionException( "failed to get report for " + pluginKey, e );
157         }
158 
159         return reportExecutions;
160     }
161 
162     protected List<MavenReportExecution> buildReportPlugin( MavenReportExecutorRequest mavenReportExecutorRequest,
163                                                             ReportPlugin reportPlugin )
164         throws Exception
165     {
166         // step 1: prepare the plugin
167         Plugin plugin = new Plugin();
168         plugin.setGroupId( reportPlugin.getGroupId() );
169         plugin.setArtifactId( reportPlugin.getArtifactId() );
170         plugin.setVersion( resolvePluginVersion( reportPlugin, mavenReportExecutorRequest ) );
171         logger.info( "configuring report plugin " + plugin.getId() );
172 
173         mergePluginToReportPlugin( mavenReportExecutorRequest, plugin, reportPlugin );
174 
175         PluginDescriptor pluginDescriptor =
176             mavenPluginManagerHelper.getPluginDescriptor( plugin, mavenReportExecutorRequest.getMavenSession() );
177 
178         // step 2: prepare the goals
179         List<GoalWithConf> goalsWithConfiguration = new ArrayList<GoalWithConf>();
180         boolean hasUserDefinedReports = prepareGoals( reportPlugin, pluginDescriptor, goalsWithConfiguration );
181 
182         // step 3: prepare the reports
183         List<MavenReportExecution> reports = new ArrayList<MavenReportExecution>();
184         for ( GoalWithConf report : goalsWithConfiguration )
185         {
186             MavenReportExecution mavenReportExecution =
187                 prepareReportExecution( mavenReportExecutorRequest, report, hasUserDefinedReports );
188 
189             if ( mavenReportExecution != null )
190             {
191                 // ok, report is ready to generate
192                 reports.add( mavenReportExecution );
193             }
194         }
195 
196         return reports;
197     }
198 
199     private boolean prepareGoals( ReportPlugin reportPlugin, PluginDescriptor pluginDescriptor,
200                                List<GoalWithConf> goalsWithConfiguration )
201     {
202         if ( reportPlugin.getReportSets().isEmpty() && reportPlugin.getReports().isEmpty() )
203         {
204             // by default, use every goal, which will be filtered later to only keep reporting goals
205             List<MojoDescriptor> mojoDescriptors = pluginDescriptor.getMojos();
206             for ( MojoDescriptor mojoDescriptor : mojoDescriptors )
207             {
208                 goalsWithConfiguration.add( new GoalWithConf( reportPlugin, pluginDescriptor, mojoDescriptor.getGoal(),
209                                                               mojoDescriptor.getConfiguration() ) );
210             }
211 
212             return false;
213         }
214 
215         Set<String> goals = new HashSet<String>();
216         for ( String report : reportPlugin.getReports() )
217         {
218             if ( goals.add( report ) )
219             {
220                 goalsWithConfiguration.add( new GoalWithConf( reportPlugin, pluginDescriptor, report,
221                                                               reportPlugin.getConfiguration() ) );
222             }
223             else
224             {
225                 logger.warn( report + " report is declared twice in default reports" );
226             }
227         }
228 
229         for ( ReportSet reportSet : reportPlugin.getReportSets() )
230         {
231             goals = new HashSet<String>();
232             for ( String report : reportSet.getReports() )
233             {
234                 if ( goals.add( report ) )
235                 {
236                     goalsWithConfiguration.add( new GoalWithConf( reportPlugin, pluginDescriptor, report,
237                                                                   reportSet.getConfiguration() ) );
238                 }
239                 else
240                 {
241                     logger.warn( report + " report is declared twice in " + reportSet.getId() + " reportSet" );
242                 }
243             }
244         }
245 
246         return true;
247     }
248 
249     private MavenReportExecution prepareReportExecution( MavenReportExecutorRequest mavenReportExecutorRequest,
250                                                          GoalWithConf report, boolean hasUserDefinedReports )
251         throws Exception
252     {
253         ReportPlugin reportPlugin = report.getReportPlugin();
254         PluginDescriptor pluginDescriptor = report.getPluginDescriptor();
255 
256         MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( report.getGoal() );
257         if ( mojoDescriptor == null )
258         {
259             throw new MojoNotFoundException( report.getGoal(), pluginDescriptor );
260         }
261 
262         MavenProject project = mavenReportExecutorRequest.getProject();
263         if ( !hasUserDefinedReports && mojoDescriptor.isAggregator() && !canAggregate( project ) )
264         {
265             // aggregator mojos automatically added from plugin are only run at execution root
266             return null;
267         }
268 
269         MojoExecution mojoExecution = new MojoExecution( pluginDescriptor.getPlugin(), report.getGoal(), null );
270 
271         mojoExecution.setMojoDescriptor( mojoDescriptor );
272 
273         mavenPluginManagerHelper.setupPluginRealm( pluginDescriptor, mavenReportExecutorRequest.getMavenSession(),
274                                                    Thread.currentThread().getContextClassLoader(), IMPORTS,
275                                                    EXCLUDES );
276 
277         if ( !isMavenReport( mojoExecution, pluginDescriptor ) )
278         {
279             if ( hasUserDefinedReports )
280             {
281                 // reports were explicitly written in the POM
282                 logger.warn( "ignoring " + mojoExecution.getPlugin().getId() + ':' + report.getGoal()
283                     + " goal since it is not a report: should be removed from reporting configuration in POM" );
284             }
285             return null;
286         }
287 
288         Xpp3Dom pluginMgmtConfiguration = null;
289         if ( project.getBuild() != null && project.getBuild().getPluginManagement() != null )
290         {
291             Plugin pluginMgmt = find( reportPlugin, project.getBuild().getPluginManagement().getPlugins() );
292 
293             if ( pluginMgmt != null )
294             {
295                 pluginMgmtConfiguration = (Xpp3Dom) pluginMgmt.getConfiguration();
296             }
297         }
298 
299         mojoExecution.setConfiguration( mergeConfiguration( mojoDescriptor.getMojoConfiguration(),
300                                                             pluginMgmtConfiguration,
301                                                             reportPlugin.getConfiguration(),
302                                                             report.getConfiguration(),
303                                                             mojoDescriptor.getParameterMap().keySet() ) );
304 
305         MavenReport mavenReport =
306             getConfiguredMavenReport( mojoExecution, pluginDescriptor, mavenReportExecutorRequest );
307 
308         MavenReportExecution mavenReportExecution =
309             new MavenReportExecution( report.getGoal(), mojoExecution.getPlugin(), mavenReport,
310                                       pluginDescriptor.getClassRealm() );
311 
312         lifecycleExecutor.calculateForkedExecutions( mojoExecution,
313                                                      mavenReportExecutorRequest.getMavenSession() );
314 
315         if ( !mojoExecution.getForkedExecutions().isEmpty() )
316         {
317             String reportDescription = pluginDescriptor.getArtifactId() + ":" + report.getGoal() + " report";
318 
319             String execution;
320             if ( StringUtils.isNotEmpty( mojoDescriptor.getExecutePhase() ) )
321             {
322                 // forked phase
323                 execution = "'"
324                     + ( StringUtils.isEmpty( mojoDescriptor.getExecuteLifecycle() ) ? ""
325                                     : ( '[' + mojoDescriptor.getExecuteLifecycle() + ']' ) )
326                     + mojoDescriptor.getExecutePhase() + "' forked phase execution";
327             }
328             else
329             {
330                 // forked goal
331                 execution = "'" + mojoDescriptor.getExecuteGoal() + "' forked goal execution";
332             }
333 
334             logger.info( "preparing " + reportDescription + " requires " + execution );
335 
336             lifecycleExecutor.executeForkedExecutions( mojoExecution,
337                                                        mavenReportExecutorRequest.getMavenSession() );
338 
339             logger.info( execution + " for " + reportDescription + " preparation done" );
340         }
341 
342         return mavenReportExecution;
343     }
344 
345     private boolean canAggregate( MavenProject project )
346     {
347         return project.isExecutionRoot() && "pom".equals( project.getPackaging() ) && ( project.getModules() != null )
348             && !project.getModules().isEmpty();
349     }
350 
351     private MavenReport getConfiguredMavenReport( MojoExecution mojoExecution, PluginDescriptor pluginDescriptor,
352                                                   MavenReportExecutorRequest mavenReportExecutorRequest )
353         throws PluginContainerException, PluginConfigurationException
354     {
355         try
356         {
357             Mojo mojo =
358                 mavenPluginManager.getConfiguredMojo( Mojo.class, mavenReportExecutorRequest.getMavenSession(),
359                                                       mojoExecution );
360 
361             return (MavenReport) mojo;
362         }
363         catch ( ClassCastException e )
364         {
365             getLog().warn( "skip ClassCastException " + e.getMessage() );
366             return null;
367         }
368         catch ( PluginContainerException e )
369         {
370             /**
371              * ignore old plugin which are using removed PluginRegistry [INFO] Caused by:
372              * java.lang.NoClassDefFoundError: org/apache/maven/plugin/registry/PluginRegistry
373              */
374             if ( e.getCause() != null && e.getCause() instanceof NoClassDefFoundError
375                 && e.getMessage().contains( "PluginRegistry" ) )
376             {
377                 getLog().warn( "skip NoClassDefFoundError with PluginRegistry " );
378                 // too noisy, only in debug mode + e.getMessage() );
379                 if ( getLog().isDebugEnabled() )
380                 {
381                     getLog().debug( e.getMessage(), e );
382                 }
383                 return null;
384             }
385             throw e;
386         }
387     }
388 
389     private boolean isMavenReport( MojoExecution mojoExecution, PluginDescriptor pluginDescriptor )
390     {
391         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
392 
393         // get the plugin's goal Mojo class
394         Class<?> mojoClass;
395         try
396         {
397             Thread.currentThread().setContextClassLoader( mojoExecution.getMojoDescriptor().getRealm() );
398 
399             mojoClass =
400                 pluginDescriptor.getClassRealm().loadClass( mojoExecution.getMojoDescriptor().getImplementation() );
401         }
402         catch ( ClassNotFoundException e )
403         {
404             getLog().warn( "skip ClassNotFoundException mojoExecution.goal '" + mojoExecution.getGoal() + "': "
405                                + e.getMessage(), e );
406             return false;
407         }
408         finally
409         {
410             Thread.currentThread().setContextClassLoader( originalClassLoader );
411         }
412 
413         // check if it is a report
414         try
415         {
416             Thread.currentThread().setContextClassLoader( mojoExecution.getMojoDescriptor().getRealm() );
417             MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( mojoExecution.getGoal() );
418 
419             boolean isMavenReport = MavenReport.class.isAssignableFrom( mojoClass );
420 
421             if ( getLog().isDebugEnabled() )
422             {
423                 if ( mojoDescriptor != null && mojoDescriptor.getImplementationClass() != null )
424                 {
425                     getLog().debug( "class " + mojoDescriptor.getImplementationClass().getName() + " isMavenReport: "
426                                         + isMavenReport );
427                 }
428 
429                 if ( !isMavenReport )
430                 {
431                     getLog().debug( "skip non MavenReport " + mojoExecution.getMojoDescriptor().getId() );
432                 }
433             }
434 
435             return isMavenReport;
436         }
437         catch ( LinkageError e )
438         {
439             getLog().warn( "skip LinkageError mojoExecution.goal '" + mojoExecution.getGoal() + "': " + e.getMessage(),
440                            e );
441             return false;
442         }
443         finally
444         {
445             Thread.currentThread().setContextClassLoader( originalClassLoader );
446         }
447     }
448 
449     /**
450      * Merge plugin configuration and reportset configuration to mojo configuration to get effective
451      * mojo configuration.
452      *
453      * @param mojoConf configuration done at mojo descriptor level
454      * @param pluginMgmtConfig configuration done at build.pluginManagement level
455      * @param pluginConf configuration done at reporting plugin level
456      * @param reportSetConf configuration done at reportSet level
457      * @param parameters set of supported parameters: any other parameter will be removed
458      * @return the effective configuration to be used
459      */
460     private Xpp3Dom mergeConfiguration( PlexusConfiguration mojoConf, Xpp3Dom pluginMgmtConfig,
461                                         PlexusConfiguration pluginConf, PlexusConfiguration reportSetConf,
462                                         Set<String> parameters )
463     {
464         Xpp3Dom mojoConfig = ( mojoConf != null ) ? convert( mojoConf ) : new Xpp3Dom( "configuration" );
465 
466         if ( pluginMgmtConfig != null || pluginConf != null || reportSetConf != null )
467         {
468             Xpp3Dom pluginConfig = ( pluginConf == null ) ? new Xpp3Dom( "fake" ) : convert( pluginConf );
469 
470             // merge pluginConf into reportSetConf
471             Xpp3Dom mergedConfig = Xpp3DomUtils.mergeXpp3Dom( convert( reportSetConf ), pluginConfig );
472             // then merge pluginMgmtConfig
473             mergedConfig = Xpp3DomUtils.mergeXpp3Dom( mergedConfig, pluginMgmtConfig );
474             // then merge mojoConf
475             mergedConfig = Xpp3DomUtils.mergeXpp3Dom( mergedConfig, mojoConfig );
476 
477             // clean result
478             Xpp3Dom cleanedConfig = new Xpp3Dom( "configuration" );
479             if ( mergedConfig.getChildren() != null )
480             {
481                 for ( Xpp3Dom parameter : mergedConfig.getChildren() )
482                 {
483                     if ( parameters.contains( parameter.getName() ) )
484                     {
485                         cleanedConfig.addChild( parameter );
486                     }
487                 }
488             }
489 
490             mojoConfig = cleanedConfig;
491         }
492 
493         return mojoConfig;
494     }
495 
496     private Xpp3Dom convert( PlexusConfiguration config )
497     {
498         if ( config == null )
499         {
500             return null;
501         }
502 
503         Xpp3Dom dom = new Xpp3Dom( config.getName() );
504         dom.setValue( config.getValue( null ) );
505 
506         for ( String attrib : config.getAttributeNames() )
507         {
508             dom.setAttribute( attrib, config.getAttribute( attrib, null ) );
509         }
510 
511         for ( int n = config.getChildCount(), i = 0; i < n; i++ )
512         {
513             dom.addChild( convert( config.getChild( i ) ) );
514         }
515 
516         return dom;
517     }
518 
519     private Logger getLog()
520     {
521         return logger;
522     }
523 
524     /**
525      * Resolve report plugin version. Steps to find a plugin version stop after each step if a non <code>null</code>
526      * value has been found:
527      * <ol>
528      * <li>use the one defined in the reportPlugin configuration,</li>
529      * <li>search similar (same groupId and artifactId) mojo in the build/plugins section of the pom,</li>
530      * <li>search similar (same groupId and artifactId) mojo in the build/pluginManagement section of the pom,</li>
531      * <li>ask {@link PluginVersionResolver} to get a fallback version and display a warning as it's not a recommended
532      * use.</li>
533      * </ol>
534      * 
535      * @param reportPlugin the report plugin to resolve the version
536      * @param mavenReportExecutorRequest the current report execution context
537      * @return the report plugin version
538      * @throws PluginVersionResolutionException
539      */
540     protected String resolvePluginVersion( ReportPlugin reportPlugin,
541                                            MavenReportExecutorRequest mavenReportExecutorRequest )
542         throws PluginVersionResolutionException
543     {
544         String reportPluginKey = reportPlugin.getGroupId() + ':' + reportPlugin.getArtifactId();
545         if ( getLog().isDebugEnabled() )
546         {
547             getLog().debug( "resolving version for " + reportPluginKey );
548         }
549 
550         // look for version defined in the reportPlugin configuration
551         if ( reportPlugin.getVersion() != null )
552         {
553             if ( getLog().isDebugEnabled() )
554             {
555                 logger.debug( "resolved " + reportPluginKey + " version from the reporting.plugins section: "
556                     + reportPlugin.getVersion() );
557             }
558             return reportPlugin.getVersion();
559         }
560 
561         MavenProject project = mavenReportExecutorRequest.getProject();
562 
563         // search in the build section
564         if ( project.getBuild() != null )
565         {
566             Plugin plugin = find( reportPlugin, project.getBuild().getPlugins() );
567 
568             if ( plugin != null && plugin.getVersion() != null )
569             {
570                 if ( getLog().isDebugEnabled() )
571                 {
572                     logger.debug( "resolved " + reportPluginKey + " version from the build.plugins section: "
573                         + plugin.getVersion() );
574                 }
575                 return plugin.getVersion();
576             }
577         }
578 
579         // search in pluginManagement section
580         if ( project.getBuild() != null && project.getBuild().getPluginManagement() != null )
581         {
582             Plugin plugin = find( reportPlugin, project.getBuild().getPluginManagement().getPlugins() );
583 
584             if ( plugin != null && plugin.getVersion() != null )
585             {
586                 if ( getLog().isDebugEnabled() )
587                 {
588                     logger.debug( "resolved " + reportPluginKey
589                         + " version from the build.pluginManagement.plugins section: " + plugin.getVersion() );
590                 }
591                 return plugin.getVersion();
592             }
593         }
594 
595         logger.warn( "Report plugin " + reportPluginKey + " has an empty version." );
596         logger.warn( "" );
597         logger.warn( "It is highly recommended to fix these problems"
598             + " because they threaten the stability of your build." );
599         logger.warn( "" );
600         logger.warn( "For this reason, future Maven versions might no"
601             + " longer support building such malformed projects." );
602 
603         Plugin plugin = new Plugin();
604         plugin.setGroupId( reportPlugin.getGroupId() );
605         plugin.setArtifactId( reportPlugin.getArtifactId() );
606 
607         PluginVersionRequest pluginVersionRequest =
608             new DefaultPluginVersionRequest( plugin, mavenReportExecutorRequest.getMavenSession() );
609 
610         PluginVersionResult result = pluginVersionResolver.resolve( pluginVersionRequest );
611         if ( getLog().isDebugEnabled() )
612         {
613             getLog().debug( "resolved " + reportPluginKey + " version from repository: " + result.getVersion() );
614         }
615         return result.getVersion();
616     }
617 
618     /**
619      * Search similar (same groupId and artifactId) plugin as a given report plugin.
620      * 
621      * @param reportPlugin the report plugin to search for a similar plugin
622      * @param plugins the candidate plugins
623      * @return the first similar plugin
624      */
625     private Plugin find( ReportPlugin reportPlugin, List<Plugin> plugins )
626     {
627         if ( plugins == null )
628         {
629             return null;
630         }
631         for ( Plugin plugin : plugins )
632         {
633             if ( StringUtils.equals( plugin.getArtifactId(), reportPlugin.getArtifactId() )
634                 && StringUtils.equals( plugin.getGroupId(), reportPlugin.getGroupId() ) )
635             {
636                 return plugin;
637             }
638         }
639         return null;
640     }
641 
642     /**
643      * TODO other stuff to merge ?
644      * <p>
645      * this method will "merge" some part of the plugin declaration existing in the build section to the fake plugin
646      * build for report execution:
647      * <ul>
648      * <li>dependencies</li>
649      * </ul>
650      * </p>
651      * 
652      * @param mavenReportExecutorRequest
653      * @param buildPlugin
654      * @param reportPlugin
655      */
656     private void mergePluginToReportPlugin( MavenReportExecutorRequest mavenReportExecutorRequest, Plugin buildPlugin,
657                                             ReportPlugin reportPlugin )
658     {
659         Plugin configuredPlugin = find( reportPlugin, mavenReportExecutorRequest.getProject().getBuild().getPlugins() );
660         if ( configuredPlugin != null )
661         {
662             if ( !configuredPlugin.getDependencies().isEmpty() )
663             {
664                 buildPlugin.getDependencies().addAll( configuredPlugin.getDependencies() );
665             }
666         }
667     }
668 
669     private static class GoalWithConf
670     {
671         private final String goal;
672 
673         private final PlexusConfiguration configuration;
674 
675         private final ReportPlugin reportPlugin;
676 
677         private final PluginDescriptor pluginDescriptor;
678 
679         public GoalWithConf( ReportPlugin reportPlugin, PluginDescriptor pluginDescriptor, String goal,
680                              PlexusConfiguration configuration )
681         {
682             this.reportPlugin = reportPlugin;
683             this.pluginDescriptor = pluginDescriptor;
684             this.goal = goal;
685             this.configuration = configuration;
686         }
687 
688         public ReportPlugin getReportPlugin()
689         {
690             return reportPlugin;
691         }
692 
693         public PluginDescriptor getPluginDescriptor()
694         {
695             return pluginDescriptor;
696         }
697 
698         public String getGoal()
699         {
700             return goal;
701         }
702 
703         public PlexusConfiguration getConfiguration()
704         {
705             return configuration;
706         }
707     }
708 }