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