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