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