1 package org.apache.maven.tools.plugin.generator;
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.plugin.descriptor.DuplicateMojoDescriptorException;
23 import org.apache.maven.plugin.descriptor.MojoDescriptor;
24 import org.apache.maven.plugin.descriptor.Parameter;
25 import org.apache.maven.plugin.descriptor.PluginDescriptor;
26 import org.apache.maven.plugin.descriptor.Requirement;
27 import org.apache.maven.project.MavenProject;
28 import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
29 import org.apache.maven.tools.plugin.PluginToolsRequest;
30 import org.apache.maven.tools.plugin.util.PluginUtils;
31 import org.codehaus.plexus.util.IOUtil;
32 import org.codehaus.plexus.util.PropertyUtils;
33 import org.codehaus.plexus.util.StringUtils;
34 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
35 import org.codehaus.plexus.util.xml.XMLWriter;
36 import org.objectweb.asm.ClassReader;
37 import org.objectweb.asm.ClassVisitor;
38 import org.objectweb.asm.ClassWriter;
39 import org.objectweb.asm.commons.RemappingClassAdapter;
40 import org.objectweb.asm.commons.SimpleRemapper;
41
42 import java.io.File;
43 import java.io.FileInputStream;
44 import java.io.FileOutputStream;
45 import java.io.IOException;
46 import java.io.OutputStreamWriter;
47 import java.io.Writer;
48 import java.text.SimpleDateFormat;
49 import java.util.Date;
50 import java.util.LinkedHashMap;
51 import java.util.LinkedHashSet;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Properties;
55 import java.util.Set;
56
57
58
59
60
61
62
63
64
65
66 public class PluginDescriptorGenerator
67 implements Generator
68 {
69
70
71
72
73 public void execute( File destinationDirectory, PluginToolsRequest request )
74 throws GeneratorException
75 {
76
77 File tmpPropertiesFile =
78 new File( request.getProject().getBuild().getDirectory(), "maven-plugin-help.properties" );
79
80 if ( tmpPropertiesFile.exists() )
81 {
82 Properties properties = PropertyUtils.loadProperties( tmpPropertiesFile );
83
84 String helpPackageName = properties.getProperty( "helpPackageName" );
85
86
87 if ( StringUtils.isEmpty( helpPackageName ) )
88 {
89 String helpMojoImplementation = rewriteHelpClassToMojoPackage( request );
90 if ( helpMojoImplementation != null )
91 {
92
93 rewriteDescriptor( request.getPluginDescriptor(), helpMojoImplementation );
94 }
95
96 }
97 }
98
99 try
100 {
101
102 File f = new File( destinationDirectory, "plugin.xml" );
103 writeDescriptor( f, request, false );
104
105
106 MavenProject mavenProject = request.getProject();
107 String pluginHelpFilePath =
108 "META-INF/maven/" + mavenProject.getGroupId() + "/" + mavenProject.getArtifactId()
109 + "/plugin-help.xml";
110 f = new File( request.getProject().getBuild().getOutputDirectory(), pluginHelpFilePath );
111 writeDescriptor( f, request, true );
112 }
113 catch ( IOException e )
114 {
115 throw new GeneratorException( e.getMessage(), e );
116 }
117 catch ( DuplicateMojoDescriptorException e )
118 {
119 throw new GeneratorException( e.getMessage(), e );
120 }
121 }
122
123 private String getVersion()
124 {
125 Package p = this.getClass().getPackage();
126 String version = ( p == null ) ? null : p.getSpecificationVersion();
127 return ( version == null ) ? "SNAPSHOT" : version;
128 }
129
130 public void writeDescriptor( File destinationFile, PluginToolsRequest request, boolean helpDescriptor )
131 throws IOException, DuplicateMojoDescriptorException
132 {
133 PluginDescriptor pluginDescriptor = request.getPluginDescriptor();
134
135 if ( destinationFile.exists() )
136 {
137 destinationFile.delete();
138 }
139 else
140 {
141 if ( !destinationFile.getParentFile().exists() )
142 {
143 destinationFile.getParentFile().mkdirs();
144 }
145 }
146
147 String encoding = "UTF-8";
148
149 Writer writer = null;
150 try
151 {
152 writer = new OutputStreamWriter( new FileOutputStream( destinationFile ), encoding );
153
154 XMLWriter w = new PrettyPrintXMLWriter( writer, encoding, null );
155
156 w.writeMarkup( "\n<!-- Generated by maven-plugin-tools " + getVersion() + " on "
157 + new SimpleDateFormat( "yyyy-MM-dd" ).format( new Date() ) + " -->\n\n" );
158
159 w.startElement( "plugin" );
160
161 GeneratorUtils.element( w, "name", pluginDescriptor.getName() );
162
163 GeneratorUtils.element( w, "description", pluginDescriptor.getDescription(), helpDescriptor );
164
165 GeneratorUtils.element( w, "groupId", pluginDescriptor.getGroupId() );
166
167 GeneratorUtils.element( w, "artifactId", pluginDescriptor.getArtifactId() );
168
169 GeneratorUtils.element( w, "version", pluginDescriptor.getVersion() );
170
171 GeneratorUtils.element( w, "goalPrefix", pluginDescriptor.getGoalPrefix() );
172
173 if ( !helpDescriptor )
174 {
175 GeneratorUtils.element( w, "isolatedRealm", String.valueOf( pluginDescriptor.isIsolatedRealm() ) );
176
177 GeneratorUtils.element( w, "inheritedByDefault", String.valueOf( pluginDescriptor.isInheritedByDefault() ) );
178 }
179
180 w.startElement( "mojos" );
181
182 if ( pluginDescriptor.getMojos() != null )
183 {
184 @SuppressWarnings( "unchecked" ) List<MojoDescriptor> descriptors = pluginDescriptor.getMojos();
185
186 if ( helpDescriptor )
187 {
188 PluginUtils.sortMojos( descriptors );
189 }
190
191 for ( MojoDescriptor descriptor : descriptors )
192 {
193 processMojoDescriptor( descriptor, w, helpDescriptor );
194 }
195 }
196
197 w.endElement();
198
199 if ( !helpDescriptor )
200 {
201 GeneratorUtils.writeDependencies( w, pluginDescriptor );
202 }
203
204 w.endElement();
205
206 writer.flush();
207
208 }
209 finally
210 {
211 IOUtil.close( writer );
212 }
213 }
214
215 protected void processMojoDescriptor( MojoDescriptor mojoDescriptor, XMLWriter w )
216 {
217 processMojoDescriptor( mojoDescriptor, w, false );
218 }
219
220
221
222
223
224
225 protected void processMojoDescriptor( MojoDescriptor mojoDescriptor, XMLWriter w, boolean helpDescriptor )
226 {
227 w.startElement( "mojo" );
228
229
230
231
232
233 w.startElement( "goal" );
234 w.writeText( mojoDescriptor.getGoal() );
235 w.endElement();
236
237
238
239
240
241 String description = mojoDescriptor.getDescription();
242
243 if ( description != null )
244 {
245 w.startElement( "description" );
246 if ( helpDescriptor )
247 {
248 w.writeText( GeneratorUtils.toText( mojoDescriptor.getDescription() ) );
249 }
250 else
251 {
252 w.writeText( mojoDescriptor.getDescription() );
253 }
254 w.endElement();
255 }
256
257
258
259
260
261 if ( mojoDescriptor.isDependencyResolutionRequired() != null )
262 {
263 GeneratorUtils.element( w, "requiresDependencyResolution", mojoDescriptor.isDependencyResolutionRequired() );
264 }
265
266
267
268
269
270 GeneratorUtils.element( w, "requiresDirectInvocation", String.valueOf( mojoDescriptor.isDirectInvocationOnly() ) );
271
272
273
274
275
276 GeneratorUtils.element( w, "requiresProject", String.valueOf( mojoDescriptor.isProjectRequired() ) );
277
278
279
280
281
282 GeneratorUtils.element( w, "requiresReports", String.valueOf( mojoDescriptor.isRequiresReports() ) );
283
284
285
286
287
288 GeneratorUtils.element( w, "aggregator", String.valueOf( mojoDescriptor.isAggregator() ) );
289
290
291
292
293
294 GeneratorUtils.element( w, "requiresOnline", String.valueOf( mojoDescriptor.isOnlineRequired() ) );
295
296
297
298
299
300 GeneratorUtils.element( w, "inheritedByDefault", String.valueOf( mojoDescriptor.isInheritedByDefault() ) );
301
302
303
304
305
306 if ( mojoDescriptor.getPhase() != null )
307 {
308 GeneratorUtils.element( w, "phase", mojoDescriptor.getPhase() );
309 }
310
311
312
313
314
315 if ( mojoDescriptor.getExecutePhase() != null )
316 {
317 GeneratorUtils.element( w, "executePhase", mojoDescriptor.getExecutePhase() );
318 }
319
320 if ( mojoDescriptor.getExecuteGoal() != null )
321 {
322 GeneratorUtils.element( w, "executeGoal", mojoDescriptor.getExecuteGoal() );
323 }
324
325 if ( mojoDescriptor.getExecuteLifecycle() != null )
326 {
327 GeneratorUtils.element( w, "executeLifecycle", mojoDescriptor.getExecuteLifecycle() );
328 }
329
330
331
332
333
334 w.startElement( "implementation" );
335 w.writeText( mojoDescriptor.getImplementation() );
336 w.endElement();
337
338
339
340
341
342 w.startElement( "language" );
343 w.writeText( mojoDescriptor.getLanguage() );
344 w.endElement();
345
346
347
348
349
350 if ( mojoDescriptor.getComponentConfigurator() != null )
351 {
352 w.startElement( "configurator" );
353 w.writeText( mojoDescriptor.getComponentConfigurator() );
354 w.endElement();
355 }
356
357
358
359
360
361 if ( mojoDescriptor.getComponentComposer() != null )
362 {
363 w.startElement( "composer" );
364 w.writeText( mojoDescriptor.getComponentComposer() );
365 w.endElement();
366 }
367
368
369
370
371
372 w.startElement( "instantiationStrategy" );
373 w.writeText( mojoDescriptor.getInstantiationStrategy() );
374 w.endElement();
375
376
377
378
379
380 w.startElement( "executionStrategy" );
381 w.writeText( mojoDescriptor.getExecutionStrategy() );
382 w.endElement();
383
384
385
386
387
388 if ( mojoDescriptor.getSince() != null )
389 {
390 w.startElement( "since" );
391
392 if ( StringUtils.isEmpty( mojoDescriptor.getSince() ) )
393 {
394 w.writeText( "No version given" );
395 }
396 else
397 {
398 w.writeText( mojoDescriptor.getSince() );
399 }
400
401 w.endElement();
402 }
403
404
405
406
407
408 if ( mojoDescriptor.getDeprecated() != null )
409 {
410 w.startElement( "deprecated" );
411
412 if ( StringUtils.isEmpty( mojoDescriptor.getDeprecated() ) )
413 {
414 w.writeText( "No reason given" );
415 }
416 else
417 {
418 w.writeText( mojoDescriptor.getDeprecated() );
419 }
420
421 w.endElement();
422 }
423
424
425
426
427
428 if ( mojoDescriptor instanceof ExtendedMojoDescriptor )
429 {
430 ExtendedMojoDescriptor extendedMojoDescriptor = (ExtendedMojoDescriptor) mojoDescriptor;
431 if ( extendedMojoDescriptor.getDependencyCollectionRequired() != null )
432 {
433 GeneratorUtils.element( w, "requiresDependencyCollection",
434 extendedMojoDescriptor.getDependencyCollectionRequired() );
435 }
436
437 GeneratorUtils.element( w, "threadSafe", String.valueOf( extendedMojoDescriptor.isThreadSafe() ) );
438 }
439
440
441
442
443
444 @SuppressWarnings( "unchecked" ) List<Parameter> parameters = mojoDescriptor.getParameters();
445
446 w.startElement( "parameters" );
447
448 Map<String, Requirement> requirements = new LinkedHashMap<String, Requirement>();
449
450 Set<Parameter> configuration = new LinkedHashSet<Parameter>();
451
452 if ( parameters != null )
453 {
454 if ( helpDescriptor )
455 {
456 PluginUtils.sortMojoParameters( parameters );
457 }
458
459 for ( Parameter parameter : parameters )
460 {
461 String expression = getExpression( parameter );
462
463 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) )
464 {
465
466
467
468 String role = expression.substring( "${component.".length(), expression.length() - 1 );
469
470 String roleHint = null;
471
472 int posRoleHintSeparator = role.indexOf( "#" );
473 if ( posRoleHintSeparator > 0 )
474 {
475 roleHint = role.substring( posRoleHintSeparator + 1 );
476
477 role = role.substring( 0, posRoleHintSeparator );
478 }
479
480
481 requirements.put( parameter.getName(), new Requirement( role, roleHint ) );
482 }
483 else if ( parameter.getRequirement() != null )
484 {
485 requirements.put( parameter.getName(), parameter.getRequirement() );
486 }
487 else if ( !helpDescriptor || parameter.isEditable() )
488 {
489
490
491 w.startElement( "parameter" );
492
493 GeneratorUtils.element( w, "name", parameter.getName() );
494
495 if ( parameter.getAlias() != null )
496 {
497 GeneratorUtils.element( w, "alias", parameter.getAlias() );
498 }
499
500 GeneratorUtils.element( w, "type", parameter.getType() );
501
502 if ( parameter.getSince() != null )
503 {
504 w.startElement( "since" );
505
506 if ( StringUtils.isEmpty( parameter.getSince() ) )
507 {
508 w.writeText( "No version given" );
509 }
510 else
511 {
512 w.writeText( parameter.getSince() );
513 }
514
515 w.endElement();
516 }
517
518 if ( parameter.getDeprecated() != null )
519 {
520 if ( StringUtils.isEmpty( parameter.getDeprecated() ) )
521 {
522 GeneratorUtils.element( w, "deprecated", "No reason given" );
523 }
524 else
525 {
526 GeneratorUtils.element( w, "deprecated", parameter.getDeprecated() );
527 }
528 }
529
530 if ( parameter.getImplementation() != null )
531 {
532 GeneratorUtils.element( w, "implementation", parameter.getImplementation() );
533 }
534
535 GeneratorUtils.element( w, "required", Boolean.toString( parameter.isRequired() ) );
536
537 GeneratorUtils.element( w, "editable", Boolean.toString( parameter.isEditable() ) );
538
539 GeneratorUtils.element( w, "description", parameter.getDescription(), helpDescriptor );
540
541 if ( StringUtils.isNotEmpty( parameter.getDefaultValue() )
542 || StringUtils.isNotEmpty( parameter.getExpression() ) )
543 {
544 configuration.add( parameter );
545 }
546
547 w.endElement();
548 }
549
550 }
551 }
552
553 w.endElement();
554
555
556
557
558
559 if ( !configuration.isEmpty() )
560 {
561 w.startElement( "configuration" );
562
563 for ( Parameter parameter : configuration )
564 {
565 if ( helpDescriptor && !parameter.isEditable() )
566 {
567
568 continue;
569 }
570
571 w.startElement( parameter.getName() );
572
573 String type = parameter.getType();
574 if ( type != null )
575 {
576 w.addAttribute( "implementation", type );
577 }
578
579 if ( parameter.getDefaultValue() != null )
580 {
581 w.addAttribute( "default-value", parameter.getDefaultValue() );
582 }
583
584 if ( parameter.getExpression() != null )
585 {
586 w.writeText( parameter.getExpression() );
587 }
588
589 w.endElement();
590 }
591
592 w.endElement();
593 }
594
595
596
597
598
599 if ( !requirements.isEmpty() && !helpDescriptor )
600 {
601 w.startElement( "requirements" );
602
603 for ( Map.Entry<String, Requirement> entry : requirements.entrySet() )
604 {
605 String key = entry.getKey();
606 Requirement requirement = entry.getValue();
607
608 w.startElement( "requirement" );
609
610 GeneratorUtils.element( w, "role", requirement.getRole() );
611
612 if ( requirement.getRoleHint() != null )
613 {
614 GeneratorUtils.element( w, "role-hint", requirement.getRoleHint() );
615 }
616
617 GeneratorUtils.element( w, "field-name", key );
618
619 w.endElement();
620 }
621
622 w.endElement();
623 }
624
625 w.endElement();
626 }
627
628
629
630
631
632
633
634 private String getExpression( Parameter parameter )
635 {
636 String expression = parameter.getExpression();
637 if ( StringUtils.isNotBlank( expression ) && !expression.contains( "${" ) )
638 {
639 expression = "${" + expression.trim() + "}";
640 parameter.setExpression( expression );
641 }
642 return expression;
643 }
644
645 protected String rewriteHelpClassToMojoPackage( PluginToolsRequest request )
646 throws GeneratorException
647 {
648 String destinationPackage = GeneratorUtils.discoverPackageName( request.getPluginDescriptor() );
649 if ( StringUtils.isEmpty( destinationPackage ) )
650 {
651 return null;
652 }
653 File helpClassFile = new File( request.getProject().getBuild().getOutputDirectory(), "HelpMojo.class" );
654 if ( !helpClassFile.exists() )
655 {
656 return null;
657 }
658 File rewriteHelpClassFile = new File(
659 request.getProject().getBuild().getOutputDirectory() + "/" + StringUtils.replace( destinationPackage, ".",
660 "/" ), "HelpMojo.class" );
661 if ( !rewriteHelpClassFile.getParentFile().exists() )
662 {
663 rewriteHelpClassFile.getParentFile().mkdirs();
664 }
665
666 ClassReader cr = null;
667 try
668 {
669 cr = new ClassReader( new FileInputStream( helpClassFile ) );
670 }
671 catch ( IOException e )
672 {
673 throw new GeneratorException( e.getMessage(), e );
674 }
675
676 ClassWriter cw = new ClassWriter( 0 );
677
678 ClassVisitor cv = new RemappingClassAdapter( cw, new SimpleRemapper( "HelpMojo",
679 StringUtils.replace( destinationPackage,
680 ".", "/" )
681 + "/HelpMojo" ) );
682
683 try
684 {
685 cr.accept( cv, ClassReader.EXPAND_FRAMES );
686 }
687 catch ( Throwable e )
688 {
689 throw new GeneratorException( "ASM issue processing classFile " + helpClassFile.getPath(), e );
690 }
691
692 byte[] renamedClass = cw.toByteArray();
693 FileOutputStream fos = null;
694 try
695 {
696 fos = new FileOutputStream( rewriteHelpClassFile );
697 fos.write( renamedClass );
698 }
699 catch ( IOException e )
700 {
701 throw new GeneratorException( "Error rewriting help class: " + e.getMessage(), e );
702 }
703 finally
704 {
705 IOUtil.close( fos );
706 }
707 helpClassFile.delete();
708 return destinationPackage + ".HelpMojo";
709 }
710
711
712 private void rewriteDescriptor( PluginDescriptor pluginDescriptor, String helpMojoImplementation )
713 {
714 MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( "help" );
715 if ( mojoDescriptor != null )
716 {
717 mojoDescriptor.setImplementation( helpMojoImplementation );
718 }
719 }
720 }