1 package org.apache.maven.reporting.exec;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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
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
180 List<GoalWithConf> goalsWithConfiguration = new ArrayList<GoalWithConf>();
181 boolean hasUserDefinedReports = prepareGoals( reportPlugin, pluginDescriptor, goalsWithConfiguration );
182
183
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
193 reports.add( mavenReportExecution );
194 }
195 }
196
197 if ( !reports.isEmpty() )
198 {
199
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
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
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
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
341 execution = "'"
342 + ( StringUtils.isEmpty( mojoDescriptor.getExecuteLifecycle() ) ? ""
343 : ( '[' + mojoDescriptor.getExecuteLifecycle() + ']' ) )
344 + mojoDescriptor.getExecutePhase() + "' forked phase execution";
345 }
346 else
347 {
348
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
390
391
392 if ( e.getCause() != null && e.getCause() instanceof NoClassDefFoundError
393 && e.getMessage().contains( "PluginRegistry" ) )
394 {
395 getLog().warn( "skip NoClassDefFoundError with PluginRegistry " );
396
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
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
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
469
470
471
472
473
474
475
476
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
489 Xpp3Dom mergedConfig = Xpp3DomUtils.mergeXpp3Dom( convert( reportSetConf ), pluginConfig );
490
491 mergedConfig = Xpp3DomUtils.mergeXpp3Dom( mergedConfig, pluginMgmtConfig );
492
493 mergedConfig = Xpp3DomUtils.mergeXpp3Dom( mergedConfig, mojoConfig );
494
495
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
544
545
546
547
548
549
550
551
552
553
554
555
556
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
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
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
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
638
639
640
641
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
662
663
664
665
666
667
668
669
670
671
672
673
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 }