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