1 package org.apache.maven.plugin.plugin.report;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.Reader;
25 import java.nio.file.Files;
26 import java.util.ArrayList;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.Optional;
32 import java.util.ResourceBundle;
33
34 import org.apache.maven.doxia.sink.Sink;
35 import org.apache.maven.model.Plugin;
36 import org.apache.maven.model.Prerequisites;
37 import org.apache.maven.plugin.descriptor.MojoDescriptor;
38 import org.apache.maven.plugin.descriptor.PluginDescriptor;
39 import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
40 import org.apache.maven.plugins.annotations.Component;
41 import org.apache.maven.plugins.annotations.Execute;
42 import org.apache.maven.plugins.annotations.LifecyclePhase;
43 import org.apache.maven.plugins.annotations.Mojo;
44 import org.apache.maven.plugins.annotations.Parameter;
45 import org.apache.maven.plugins.plugin.descriptor.EnhancedPluginDescriptorBuilder;
46 import org.apache.maven.project.MavenProject;
47 import org.apache.maven.reporting.AbstractMavenReport;
48 import org.apache.maven.reporting.AbstractMavenReportRenderer;
49 import org.apache.maven.reporting.MavenReportException;
50 import org.apache.maven.rtinfo.RuntimeInformation;
51 import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
52 import org.apache.maven.tools.plugin.PluginToolsRequest;
53 import org.apache.maven.tools.plugin.generator.GeneratorException;
54 import org.apache.maven.tools.plugin.generator.GeneratorUtils;
55 import org.apache.maven.tools.plugin.generator.PluginXdocGenerator;
56 import org.apache.maven.tools.plugin.util.PluginUtils;
57 import org.codehaus.plexus.configuration.PlexusConfigurationException;
58 import org.codehaus.plexus.util.StringUtils;
59 import org.codehaus.plexus.util.xml.XmlStreamReader;
60 import org.codehaus.plexus.util.xml.Xpp3Dom;
61
62
63
64
65
66
67
68
69
70
71 @Mojo( name = "report", threadSafe = true )
72 @Execute( phase = LifecyclePhase.PROCESS_CLASSES )
73 public class PluginReport
74 extends AbstractMavenReport
75 {
76
77
78
79
80
81 @Parameter( defaultValue = "${project.build.directory}/generated-site/xdoc" )
82 private File outputDirectory;
83
84
85
86
87
88
89 @Parameter( defaultValue = "false", property = "maven.plugin.report.skip" )
90 private boolean skip;
91
92
93
94
95
96
97
98 @Parameter( defaultValue = "false", property = "maven.plugin.report.hasExtensionsToLoad" )
99 private boolean hasExtensionsToLoad;
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 @Parameter
119 private List<RequirementsHistory> requirementsHistories = new ArrayList<>();
120
121 @Component
122 private RuntimeInformation rtInfo;
123
124
125
126
127
128
129 @Parameter( defaultValue = "${project.build.directory}/plugin-enhanced.xml", required = true,
130 readonly = true )
131 private File enhancedPluginXmlFile;
132
133
134
135
136
137
138
139
140
141
142
143 @Parameter( property = "maven.plugin.report.disableInternalJavadocLinkValidation" )
144 private boolean disableInternalJavadocLinkValidation;
145
146
147
148
149 @Override
150 protected String getOutputDirectory()
151 {
152
153 return project.getReporting().getOutputDirectory();
154 }
155
156
157
158
159 @Override
160 public boolean canGenerateReport()
161 {
162 return enhancedPluginXmlFile != null && enhancedPluginXmlFile.isFile() && enhancedPluginXmlFile.canRead();
163 }
164
165
166
167
168 @Override
169 protected void executeReport( Locale locale )
170 throws MavenReportException
171 {
172 if ( skip )
173 {
174 getLog().info( "Maven Plugin Plugin Report generation skipped." );
175 return;
176 }
177
178 PluginDescriptor pluginDescriptor = extractPluginDescriptor();
179
180
181 generateMojosDocumentation( pluginDescriptor, locale );
182
183
184 PluginOverviewRenderer r =
185 new PluginOverviewRenderer( getProject(), requirementsHistories, getSink(),
186 pluginDescriptor, locale, hasExtensionsToLoad );
187 r.render();
188 }
189
190 private PluginDescriptor extractPluginDescriptor()
191 throws MavenReportException
192 {
193 PluginDescriptorBuilder builder = new EnhancedPluginDescriptorBuilder( rtInfo );
194
195 try ( Reader input = new XmlStreamReader( Files.newInputStream( enhancedPluginXmlFile.toPath() ) ) )
196 {
197 return builder.build( input );
198 }
199 catch ( IOException | PlexusConfigurationException e )
200 {
201 throw new MavenReportException( "Error extracting plugin descriptor from " + enhancedPluginXmlFile, e );
202 }
203
204 }
205
206
207
208
209 @Override
210 public String getDescription( Locale locale )
211 {
212 return getBundle( locale ).getString( "report.plugin.description" );
213 }
214
215
216
217
218 @Override
219 public String getName( Locale locale )
220 {
221 return getBundle( locale ).getString( "report.plugin.name" );
222 }
223
224
225
226
227 @Override
228 public String getOutputName()
229 {
230 return "plugin-info";
231 }
232
233
234
235
236
237
238
239
240 private void generateMojosDocumentation( PluginDescriptor pluginDescriptor, Locale locale )
241 throws MavenReportException
242 {
243 try
244 {
245 File outputDir = outputDirectory;
246 outputDir.mkdirs();
247
248 PluginXdocGenerator generator = new PluginXdocGenerator( getProject(), locale, getReportOutputDirectory(),
249 disableInternalJavadocLinkValidation );
250 PluginToolsRequest pluginToolsRequest = new DefaultPluginToolsRequest( getProject(), pluginDescriptor );
251 generator.execute( outputDir, pluginToolsRequest );
252 }
253 catch ( GeneratorException e )
254 {
255 throw new MavenReportException( "Error writing plugin documentation", e );
256 }
257
258 }
259
260
261
262
263
264 protected static ResourceBundle getBundle( Locale locale )
265 {
266 return ResourceBundle.getBundle( "plugin-report", locale, PluginReport.class.getClassLoader() );
267 }
268
269
270
271
272
273 static class PluginOverviewRenderer
274 extends AbstractMavenReportRenderer
275 {
276 private final MavenProject project;
277
278 private final List<RequirementsHistory> requirementsHistories;
279
280 private final PluginDescriptor pluginDescriptor;
281
282 private final Locale locale;
283
284 private final boolean hasExtensionsToLoad;
285
286
287
288
289
290
291
292
293 PluginOverviewRenderer( MavenProject project,
294 List<RequirementsHistory> requirementsHistories, Sink sink,
295 PluginDescriptor pluginDescriptor, Locale locale, boolean hasExtensionsToLoad )
296 {
297 super( sink );
298
299 this.project = project;
300
301 this.requirementsHistories = requirementsHistories;
302
303 this.pluginDescriptor = pluginDescriptor;
304
305 this.locale = locale;
306
307 this.hasExtensionsToLoad = hasExtensionsToLoad;
308 }
309
310
311
312
313 @Override
314 public String getTitle()
315 {
316 return getBundle( locale ).getString( "report.plugin.title" );
317 }
318
319
320
321
322 @Override
323 public void renderBody()
324 {
325 startSection( getTitle() );
326
327 if ( !( pluginDescriptor.getMojos() != null && pluginDescriptor.getMojos().size() > 0 ) )
328 {
329 paragraph( getBundle( locale ).getString( "report.plugin.goals.nogoal" ) );
330 endSection();
331 return;
332 }
333
334 paragraph( getBundle( locale ).getString( "report.plugin.goals.intro" ) );
335
336 boolean hasMavenReport = false;
337 for ( Iterator<MojoDescriptor> i = pluginDescriptor.getMojos().iterator(); i.hasNext(); )
338 {
339 MojoDescriptor mojo = i.next();
340
341 if ( GeneratorUtils.isMavenReport( mojo.getImplementation(), project ) )
342 {
343 hasMavenReport = true;
344 }
345 }
346
347 startTable();
348
349 String goalColumnName = getBundle( locale ).getString( "report.plugin.goals.column.goal" );
350 String isMavenReport = getBundle( locale ).getString( "report.plugin.goals.column.isMavenReport" );
351 String descriptionColumnName = getBundle( locale ).getString( "report.plugin.goals.column.description" );
352 if ( hasMavenReport )
353 {
354 tableHeader( new String[] {goalColumnName, isMavenReport, descriptionColumnName} );
355 }
356 else
357 {
358 tableHeader( new String[] {goalColumnName, descriptionColumnName} );
359 }
360
361 List<MojoDescriptor> mojos = new ArrayList<>();
362 mojos.addAll( pluginDescriptor.getMojos() );
363 PluginUtils.sortMojos( mojos );
364 for ( MojoDescriptor mojo : mojos )
365 {
366 String goalName = mojo.getFullGoalName();
367
368
369
370
371
372 String goalDocumentationLink = "./" + mojo.getGoal() + "-mojo.html";
373
374 String description;
375 if ( StringUtils.isNotEmpty( mojo.getDeprecated() ) )
376 {
377 description =
378 "<strong>" + getBundle( locale ).getString( "report.plugin.goal.deprecated" ) + "</strong> "
379 + mojo.getDeprecated();
380 }
381 else if ( StringUtils.isNotEmpty( mojo.getDescription() ) )
382 {
383 description = mojo.getDescription();
384 }
385 else
386 {
387 description = getBundle( locale ).getString( "report.plugin.goal.nodescription" );
388 }
389
390 sink.tableRow();
391 tableCell( createLinkPatternedText( goalName, goalDocumentationLink ) );
392 if ( hasMavenReport )
393 {
394 if ( GeneratorUtils.isMavenReport( mojo.getImplementation(), project ) )
395 {
396 sink.tableCell();
397 sink.text( getBundle( locale ).getString( "report.plugin.isReport" ) );
398 sink.tableCell_();
399 }
400 else
401 {
402 sink.tableCell();
403 sink.text( getBundle( locale ).getString( "report.plugin.isNotReport" ) );
404 sink.tableCell_();
405 }
406 }
407 tableCell( description, true );
408 sink.tableRow_();
409 }
410
411 endTable();
412
413 startSection( getBundle( locale ).getString( "report.plugin.systemrequirements" ) );
414
415 paragraph( getBundle( locale ).getString( "report.plugin.systemrequirements.intro" ) );
416
417 startTable();
418
419 String maven = discoverMavenRequirement( project );
420 sink.tableRow();
421 tableCell( getBundle( locale ).getString( "report.plugin.systemrequirements.maven" ) );
422 tableCell( ( maven != null
423 ? maven
424 : getBundle( locale ).getString( "report.plugin.systemrequirements.nominimum" ) ) );
425 sink.tableRow_();
426
427 String jdk = discoverJdkRequirement( project );
428 sink.tableRow();
429 tableCell( getBundle( locale ).getString( "report.plugin.systemrequirements.jdk" ) );
430 tableCell(
431 ( jdk != null ? jdk : getBundle( locale ).getString( "report.plugin.systemrequirements.nominimum" ) ) );
432 sink.tableRow_();
433
434 endTable();
435
436 endSection();
437
438 renderRequirementsHistories();
439
440 renderUsageSection( hasMavenReport );
441
442 endSection();
443 }
444
445 private void renderRequirementsHistories()
446 {
447 if ( requirementsHistories.isEmpty() )
448 {
449 return;
450 }
451
452 startSection( getBundle( locale ).getString( "report.plugin.systemrequirements.history" ) );
453 paragraph( getBundle( locale ).getString( "report.plugin.systemrequirements.history.intro" ) );
454
455 startTable();
456 tableHeader( new String[] {
457 getBundle( locale ).getString( "report.plugin.systemrequirements.history.version" ),
458 getBundle( locale ).getString( "report.plugin.systemrequirements.history.maven" ),
459 getBundle( locale ).getString( "report.plugin.systemrequirements.history.jdk" )
460 } );
461
462 requirementsHistories.forEach(
463 requirementsHistory ->
464 {
465 sink.tableRow();
466 tableCell( requirementsHistory.getVersion() );
467 tableCell( requirementsHistory.getMaven() );
468 tableCell( requirementsHistory.getJdk() );
469 sink.tableRow_();
470 } );
471 endTable();
472
473 endSection();
474 }
475
476
477
478
479
480
481 private void renderUsageSection( boolean hasMavenReport )
482 {
483 startSection( getBundle( locale ).getString( "report.plugin.usage" ) );
484
485
486 sink.paragraph();
487 text( getBundle( locale ).getString( "report.plugin.usage.intro" ) );
488 sink.paragraph_();
489
490 StringBuilder sb = new StringBuilder();
491 sb.append( "<project>" ).append( '\n' );
492 sb.append( " ..." ).append( '\n' );
493 sb.append( " <build>" ).append( '\n' );
494 sb.append(
495 " <!-- " + getBundle( locale ).getString( "report.plugin.usage.pluginManagement" ) + " -->" ).append(
496 '\n' );
497 sb.append( " <pluginManagement>" ).append( '\n' );
498 sb.append( " <plugins>" ).append( '\n' );
499 sb.append( " <plugin>" ).append( '\n' );
500 sb.append( " <groupId>" ).append( pluginDescriptor.getGroupId() ).append( "</groupId>" ).append(
501 '\n' );
502 sb.append( " <artifactId>" ).append( pluginDescriptor.getArtifactId() ).append(
503 "</artifactId>" ).append( '\n' );
504 sb.append( " <version>" ).append( pluginDescriptor.getVersion() ).append( "</version>" ).append(
505 '\n' );
506 if ( hasExtensionsToLoad )
507 {
508 sb.append( " <extensions>true</extensions>" ).append(
509 '\n' );
510 }
511 sb.append( " </plugin>" ).append( '\n' );
512 sb.append( " ..." ).append( '\n' );
513 sb.append( " </plugins>" ).append( '\n' );
514 sb.append( " </pluginManagement>" ).append( '\n' );
515 sb.append( " <!-- " + getBundle( locale ).getString( "report.plugin.usage.plugins" ) + " -->" ).append(
516 '\n' );
517 sb.append( " <plugins>" ).append( '\n' );
518 sb.append( " <plugin>" ).append( '\n' );
519 sb.append( " <groupId>" ).append( pluginDescriptor.getGroupId() ).append( "</groupId>" ).append(
520 '\n' );
521 sb.append( " <artifactId>" ).append( pluginDescriptor.getArtifactId() ).append(
522 "</artifactId>" ).append( '\n' );
523 sb.append( " </plugin>" ).append( '\n' );
524 sb.append( " ..." ).append( '\n' );
525 sb.append( " </plugins>" ).append( '\n' );
526 sb.append( " </build>" ).append( '\n' );
527
528 if ( hasMavenReport )
529 {
530 sb.append( " ..." ).append( '\n' );
531 sb.append(
532 " <!-- " + getBundle( locale ).getString( "report.plugin.usage.reporting" ) + " -->" ).append(
533 '\n' );
534 sb.append( " <reporting>" ).append( '\n' );
535 sb.append( " <plugins>" ).append( '\n' );
536 sb.append( " <plugin>" ).append( '\n' );
537 sb.append( " <groupId>" ).append( pluginDescriptor.getGroupId() ).append( "</groupId>" ).append(
538 '\n' );
539 sb.append( " <artifactId>" ).append( pluginDescriptor.getArtifactId() ).append(
540 "</artifactId>" ).append( '\n' );
541 sb.append( " <version>" ).append( pluginDescriptor.getVersion() ).append( "</version>" ).append(
542 '\n' );
543 sb.append( " </plugin>" ).append( '\n' );
544 sb.append( " ..." ).append( '\n' );
545 sb.append( " </plugins>" ).append( '\n' );
546 sb.append( " </reporting>" ).append( '\n' );
547 }
548
549 sb.append( " ..." ).append( '\n' );
550 sb.append( "</project>" ).append( '\n' );
551
552 verbatimText( sb.toString() );
553
554 sink.paragraph();
555 linkPatternedText( getBundle( locale ).getString( "report.plugin.configuration.end" ) );
556 sink.paragraph_();
557
558 endSection();
559 }
560
561
562
563
564
565
566
567 private static String discoverMavenRequirement( MavenProject project )
568 {
569 return Optional.ofNullable( project.getPrerequisites() )
570 .map( Prerequisites::getMaven )
571 .orElse( null );
572 }
573
574
575
576
577
578
579
580
581
582
583
584
585
586 private static String discoverJdkRequirement( MavenProject project )
587 {
588
589 Plugin compiler = getCompilerPlugin( project.getBuild().getPluginsAsMap() );
590 if ( compiler == null )
591 {
592 compiler = getCompilerPlugin( project.getPluginManagement().getPluginsAsMap() );
593 }
594
595 String jdk = getPluginParameter( compiler, "release" );
596 if ( jdk != null )
597 {
598 return jdk;
599 }
600
601 jdk = project.getProperties().getProperty( "maven.compiler.release" );
602 if ( jdk != null )
603 {
604 return jdk;
605 }
606
607 jdk = getPluginParameter( compiler, "target" );
608 if ( jdk != null )
609 {
610 return jdk;
611 }
612
613
614 jdk = project.getProperties().getProperty( "maven.compiler.target" );
615 if ( jdk != null )
616 {
617 return jdk;
618 }
619
620 String version = ( compiler == null ) ? null : compiler.getVersion();
621
622 if ( version != null )
623 {
624 return "Default target for maven-compiler-plugin version " + version;
625 }
626
627 return null;
628 }
629
630 private static Plugin getCompilerPlugin( Map<String, Plugin> pluginsAsMap )
631 {
632 return pluginsAsMap.get( "org.apache.maven.plugins:maven-compiler-plugin" );
633 }
634
635 private static String getPluginParameter( Plugin plugin, String parameter )
636 {
637 if ( plugin != null )
638 {
639 Xpp3Dom pluginConf = (Xpp3Dom) plugin.getConfiguration();
640
641 if ( pluginConf != null )
642 {
643 Xpp3Dom target = pluginConf.getChild( parameter );
644
645 if ( target != null )
646 {
647 return target.getValue();
648 }
649 }
650 }
651
652 return null;
653 }
654 }
655 }