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