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