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