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