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.logging.Logger;
54 import org.codehaus.plexus.util.xml.Xpp3Dom;
55 import org.codehaus.plexus.util.xml.Xpp3DomUtils;
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
97 @Component( role = MavenReportExecutor.class )
98 public class DefaultMavenReportExecutor
99 implements MavenReportExecutor
100 {
101 @Requirement
102 private Logger logger;
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 getLog().debug( "DefaultMavenReportExecutor.buildMavenReports()" );
141
142 Set<String> reportPluginKeys = new HashSet<>();
143 List<MavenReportExecution> reportExecutions = new ArrayList<>();
144
145 String pluginKey = "";
146 try
147 {
148 for ( ReportPlugin reportPlugin : mavenReportExecutorRequest.getReportPlugins() )
149 {
150 pluginKey = reportPlugin.getGroupId() + ':' + reportPlugin.getArtifactId();
151
152 if ( !reportPluginKeys.add( pluginKey ) )
153 {
154 logger.info( "plugin " + pluginKey + " will be executed more than one time" );
155 }
156
157 reportExecutions.addAll( buildReportPlugin( mavenReportExecutorRequest, reportPlugin ) );
158 }
159 }
160 catch ( Exception e )
161 {
162 throw new MojoExecutionException( "failed to get report for " + pluginKey, e );
163 }
164
165 return reportExecutions;
166 }
167
168 protected List<MavenReportExecution> buildReportPlugin( MavenReportExecutorRequest mavenReportExecutorRequest,
169 ReportPlugin reportPlugin )
170 throws Exception
171 {
172
173 Plugin plugin = new Plugin();
174 plugin.setGroupId( reportPlugin.getGroupId() );
175 plugin.setArtifactId( reportPlugin.getArtifactId() );
176 plugin.setVersion( resolvePluginVersion( reportPlugin, mavenReportExecutorRequest ) );
177 logger.info( "configuring report plugin " + plugin.getId() );
178
179 mergePluginToReportPlugin( mavenReportExecutorRequest, plugin, reportPlugin );
180
181 PluginDescriptor pluginDescriptor =
182 mavenPluginManagerHelper.getPluginDescriptor( plugin, mavenReportExecutorRequest.getMavenSession() );
183
184
185 List<GoalWithConf> goalsWithConfiguration = new ArrayList<>();
186 boolean hasUserDefinedReports = prepareGoals( reportPlugin, pluginDescriptor, goalsWithConfiguration );
187
188
189 List<MavenReportExecution> reports = new ArrayList<>( goalsWithConfiguration.size() );
190 for ( GoalWithConf report : goalsWithConfiguration )
191 {
192 MavenReportExecution mavenReportExecution =
193 prepareReportExecution( mavenReportExecutorRequest, report, hasUserDefinedReports );
194
195 if ( mavenReportExecution != null )
196 {
197
198 reports.add( mavenReportExecution );
199 }
200 }
201
202 if ( !reports.isEmpty() )
203 {
204
205 StringBuilder buff = new StringBuilder();
206 for ( MavenReportExecution mre : reports )
207 {
208 if ( buff.length() > 0 )
209 {
210 buff.append( ", " );
211 }
212 buff.append( mre.getGoal() );
213 }
214 logger.info( reports.size() + " report" + ( reports.size() > 1 ? "s" : "" ) + " "
215 + ( hasUserDefinedReports ? "configured" : "detected" ) + " for " + plugin.getArtifactId() + ":"
216 + plugin.getVersion() + ": " + buff );
217 }
218
219 return reports;
220 }
221
222 private boolean prepareGoals( ReportPlugin reportPlugin, PluginDescriptor pluginDescriptor,
223 List<GoalWithConf> goalsWithConfiguration )
224 {
225 if ( reportPlugin.getReportSets().isEmpty() && reportPlugin.getReports().isEmpty() )
226 {
227
228 List<MojoDescriptor> mojoDescriptors = pluginDescriptor.getMojos();
229 for ( MojoDescriptor mojoDescriptor : mojoDescriptors )
230 {
231 goalsWithConfiguration.add( new GoalWithConf( reportPlugin, pluginDescriptor, mojoDescriptor.getGoal(),
232 mojoDescriptor.getConfiguration() ) );
233 }
234
235 return false;
236 }
237
238 Set<String> goals = new HashSet<>();
239 for ( String report : reportPlugin.getReports() )
240 {
241 if ( goals.add( report ) )
242 {
243 goalsWithConfiguration.add( new GoalWithConf( reportPlugin, pluginDescriptor, report,
244 reportPlugin.getConfiguration() ) );
245 }
246 else
247 {
248 logger.warn( report + " report is declared twice in default reports" );
249 }
250 }
251
252 for ( ReportSet reportSet : reportPlugin.getReportSets() )
253 {
254 goals = new HashSet<>();
255 for ( String report : reportSet.getReports() )
256 {
257 if ( goals.add( report ) )
258 {
259 goalsWithConfiguration.add( new GoalWithConf( reportPlugin, pluginDescriptor, report,
260 reportSet.getConfiguration() ) );
261 }
262 else
263 {
264 logger.warn( report + " report is declared twice in " + reportSet.getId() + " reportSet" );
265 }
266 }
267 }
268
269 return true;
270 }
271
272 private MavenReportExecution prepareReportExecution( MavenReportExecutorRequest mavenReportExecutorRequest,
273 GoalWithConf report, boolean hasUserDefinedReports )
274 throws Exception
275 {
276 ReportPlugin reportPlugin = report.getReportPlugin();
277 PluginDescriptor pluginDescriptor = report.getPluginDescriptor();
278
279 MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( report.getGoal() );
280 if ( mojoDescriptor == null )
281 {
282 throw new MojoNotFoundException( report.getGoal(), pluginDescriptor );
283 }
284
285 MavenProject project = mavenReportExecutorRequest.getProject();
286 if ( !hasUserDefinedReports && mojoDescriptor.isAggregator() && !canAggregate( project ) )
287 {
288
289 return null;
290 }
291
292 MojoExecution mojoExecution = new MojoExecution( pluginDescriptor.getPlugin(), report.getGoal(), null );
293
294 mojoExecution.setMojoDescriptor( mojoDescriptor );
295
296 mavenPluginManagerHelper.setupPluginRealm( pluginDescriptor, mavenReportExecutorRequest.getMavenSession(),
297 Thread.currentThread().getContextClassLoader(), IMPORTS,
298 EXCLUDES );
299
300 if ( !isMavenReport( mojoExecution, pluginDescriptor ) )
301 {
302 if ( hasUserDefinedReports )
303 {
304
305 logger.warn( "ignoring " + mojoExecution.getPlugin().getId() + ':' + report.getGoal()
306 + " goal since it is not a report: should be removed from reporting configuration in POM" );
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 " + reportDescription + " requires " + execution );
358
359 lifecycleExecutor.executeForkedExecutions( mojoExecution,
360 mavenReportExecutorRequest.getMavenSession() );
361
362 logger.info( execution + " for " + reportDescription + " preparation done" );
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 getLog().warn( "skip ClassCastException " + e.getMessage() );
389 return null;
390 }
391 catch ( PluginContainerException e )
392 {
393
394
395
396
397 if ( e.getCause() != null && e.getCause() instanceof NoClassDefFoundError
398 && e.getMessage().contains( "PluginRegistry" ) )
399 {
400 getLog().warn( "skip NoClassDefFoundError with PluginRegistry " );
401
402 if ( getLog().isDebugEnabled() )
403 {
404 getLog().debug( e.getMessage(), e );
405 }
406 return null;
407 }
408 throw e;
409 }
410 }
411
412 private boolean isMavenReport( MojoExecution mojoExecution, PluginDescriptor pluginDescriptor )
413 {
414 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
415
416
417 Class<?> mojoClass;
418 try
419 {
420 Thread.currentThread().setContextClassLoader( mojoExecution.getMojoDescriptor().getRealm() );
421
422 mojoClass =
423 pluginDescriptor.getClassRealm().loadClass( mojoExecution.getMojoDescriptor().getImplementation() );
424 }
425 catch ( ClassNotFoundException e )
426 {
427 getLog().warn( "skip ClassNotFoundException mojoExecution.goal '" + mojoExecution.getGoal() + "': "
428 + e.getMessage(), e );
429 return false;
430 }
431 finally
432 {
433 Thread.currentThread().setContextClassLoader( originalClassLoader );
434 }
435
436
437 try
438 {
439 Thread.currentThread().setContextClassLoader( mojoExecution.getMojoDescriptor().getRealm() );
440 MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( mojoExecution.getGoal() );
441
442 boolean isMavenReport = MavenReport.class.isAssignableFrom( mojoClass );
443
444 if ( getLog().isDebugEnabled() )
445 {
446 if ( mojoDescriptor != null && mojoDescriptor.getImplementationClass() != null )
447 {
448 getLog().debug( "class " + mojoDescriptor.getImplementationClass().getName() + " isMavenReport: "
449 + isMavenReport );
450 }
451
452 if ( !isMavenReport )
453 {
454 getLog().debug( "skip non MavenReport " + mojoExecution.getMojoDescriptor().getId() );
455 }
456 }
457
458 return isMavenReport;
459 }
460 catch ( LinkageError e )
461 {
462 getLog().warn( "skip LinkageError mojoExecution.goal '" + mojoExecution.getGoal() + "': " + e.getMessage(),
463 e );
464 return false;
465 }
466 finally
467 {
468 Thread.currentThread().setContextClassLoader( originalClassLoader );
469 }
470 }
471
472
473
474
475
476
477
478
479
480
481
482
483 private Xpp3Dom mergeConfiguration( PlexusConfiguration mojoConf, Xpp3Dom pluginMgmtConfig,
484 PlexusConfiguration pluginConf, PlexusConfiguration reportSetConf,
485 Set<String> parameters )
486 {
487 Xpp3Dom mojoConfig = ( mojoConf != null ) ? convert( mojoConf ) : new Xpp3Dom( "configuration" );
488
489 if ( pluginMgmtConfig != null || pluginConf != null || reportSetConf != null )
490 {
491 Xpp3Dom pluginConfig = ( pluginConf == null ) ? new Xpp3Dom( "fake" ) : convert( pluginConf );
492
493
494 Xpp3Dom mergedConfig = Xpp3DomUtils.mergeXpp3Dom( convert( reportSetConf ), pluginConfig );
495
496 mergedConfig = Xpp3DomUtils.mergeXpp3Dom( mergedConfig, pluginMgmtConfig );
497
498 mergedConfig = Xpp3DomUtils.mergeXpp3Dom( mergedConfig, mojoConfig );
499
500
501 Xpp3Dom cleanedConfig = new Xpp3Dom( "configuration" );
502 if ( mergedConfig.getChildren() != null )
503 {
504 for ( Xpp3Dom parameter : mergedConfig.getChildren() )
505 {
506 if ( parameters.contains( parameter.getName() ) )
507 {
508 cleanedConfig.addChild( parameter );
509 }
510 }
511 }
512
513 mojoConfig = cleanedConfig;
514 }
515
516 return mojoConfig;
517 }
518
519 private Xpp3Dom convert( PlexusConfiguration config )
520 {
521 if ( config == null )
522 {
523 return null;
524 }
525
526 Xpp3Dom dom = new Xpp3Dom( config.getName() );
527 dom.setValue( config.getValue( null ) );
528
529 for ( String attrib : config.getAttributeNames() )
530 {
531 dom.setAttribute( attrib, config.getAttribute( attrib, null ) );
532 }
533
534 for ( int n = config.getChildCount(), i = 0; i < n; i++ )
535 {
536 dom.addChild( convert( config.getChild( i ) ) );
537 }
538
539 return dom;
540 }
541
542 private Logger getLog()
543 {
544 return logger;
545 }
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563 protected String resolvePluginVersion( ReportPlugin reportPlugin,
564 MavenReportExecutorRequest mavenReportExecutorRequest )
565 throws PluginVersionResolutionException
566 {
567 String reportPluginKey = reportPlugin.getGroupId() + ':' + reportPlugin.getArtifactId();
568 if ( getLog().isDebugEnabled() )
569 {
570 getLog().debug( "resolving version for " + reportPluginKey );
571 }
572
573
574 if ( reportPlugin.getVersion() != null )
575 {
576 if ( getLog().isDebugEnabled() )
577 {
578 logger.debug( "resolved " + reportPluginKey + " version from the reporting.plugins section: "
579 + reportPlugin.getVersion() );
580 }
581 return reportPlugin.getVersion();
582 }
583
584 MavenProject project = mavenReportExecutorRequest.getProject();
585
586
587 if ( project.getBuild() != null )
588 {
589 Plugin plugin = find( reportPlugin, project.getBuild().getPlugins() );
590
591 if ( plugin != null && plugin.getVersion() != null )
592 {
593 if ( getLog().isDebugEnabled() )
594 {
595 logger.debug( "resolved " + reportPluginKey + " version from the build.plugins section: "
596 + plugin.getVersion() );
597 }
598 return plugin.getVersion();
599 }
600 }
601
602
603 if ( project.getBuild() != null && project.getBuild().getPluginManagement() != null )
604 {
605 Plugin plugin = find( reportPlugin, project.getBuild().getPluginManagement().getPlugins() );
606
607 if ( plugin != null && plugin.getVersion() != null )
608 {
609 if ( getLog().isDebugEnabled() )
610 {
611 logger.debug( "resolved " + reportPluginKey
612 + " version from the build.pluginManagement.plugins section: " + plugin.getVersion() );
613 }
614 return plugin.getVersion();
615 }
616 }
617
618 logger.warn( "Report plugin " + reportPluginKey + " has an empty version." );
619 logger.warn( "" );
620 logger.warn( "It is highly recommended to fix these problems"
621 + " because they threaten the stability of your build." );
622 logger.warn( "" );
623 logger.warn( "For this reason, future Maven versions might no"
624 + " longer support building such malformed projects." );
625
626 Plugin plugin = new Plugin();
627 plugin.setGroupId( reportPlugin.getGroupId() );
628 plugin.setArtifactId( reportPlugin.getArtifactId() );
629
630 PluginVersionRequest pluginVersionRequest =
631 new DefaultPluginVersionRequest( plugin, mavenReportExecutorRequest.getMavenSession() );
632
633 PluginVersionResult result = pluginVersionResolver.resolve( pluginVersionRequest );
634 if ( getLog().isDebugEnabled() )
635 {
636 getLog().debug( "resolved " + reportPluginKey + " version from repository: " + result.getVersion() );
637 }
638 return result.getVersion();
639 }
640
641
642
643
644
645
646
647
648 private Plugin find( ReportPlugin reportPlugin, List<Plugin> plugins )
649 {
650 if ( plugins == null )
651 {
652 return null;
653 }
654 for ( Plugin plugin : plugins )
655 {
656 if ( Objects.equals( plugin.getArtifactId(), reportPlugin.getArtifactId() )
657 && Objects.equals( plugin.getGroupId(), reportPlugin.getGroupId() ) )
658 {
659 return plugin;
660 }
661 }
662 return null;
663 }
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680 private void mergePluginToReportPlugin( MavenReportExecutorRequest mavenReportExecutorRequest, Plugin buildPlugin,
681 ReportPlugin reportPlugin )
682 {
683 Build build = mavenReportExecutorRequest.getProject().getBuild();
684 Plugin configuredPlugin = find( reportPlugin, build.getPlugins() );
685 if ( configuredPlugin == null && build.getPluginManagement() != null )
686 {
687 configuredPlugin = find( reportPlugin, build.getPluginManagement().getPlugins() );
688 }
689 if ( configuredPlugin != null )
690 {
691 if ( !configuredPlugin.getDependencies().isEmpty() )
692 {
693 buildPlugin.getDependencies().addAll( configuredPlugin.getDependencies() );
694 }
695 }
696 }
697
698 private static class GoalWithConf
699 {
700 private final String goal;
701
702 private final PlexusConfiguration configuration;
703
704 private final ReportPlugin reportPlugin;
705
706 private final PluginDescriptor pluginDescriptor;
707
708 GoalWithConf( ReportPlugin reportPlugin, PluginDescriptor pluginDescriptor, String goal,
709 PlexusConfiguration configuration )
710 {
711 this.reportPlugin = reportPlugin;
712 this.pluginDescriptor = pluginDescriptor;
713 this.goal = goal;
714 this.configuration = configuration;
715 }
716
717 public ReportPlugin getReportPlugin()
718 {
719 return reportPlugin;
720 }
721
722 public PluginDescriptor getPluginDescriptor()
723 {
724 return pluginDescriptor;
725 }
726
727 public String getGoal()
728 {
729 return goal;
730 }
731
732 public PlexusConfiguration getConfiguration()
733 {
734 return configuration;
735 }
736 }
737 }