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