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