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.MojoDescriptor;
23 import org.apache.maven.plugin.descriptor.PluginDescriptor;
24 import org.apache.maven.plugin.logging.Log;
25 import org.apache.maven.project.MavenProject;
26 import org.apache.maven.tools.plugin.PluginToolsRequest;
27 import org.apache.velocity.VelocityContext;
28 import org.codehaus.plexus.logging.AbstractLogEnabled;
29 import org.codehaus.plexus.logging.Logger;
30 import org.codehaus.plexus.logging.console.ConsoleLogger;
31 import org.codehaus.plexus.util.FileUtils;
32 import org.codehaus.plexus.util.IOUtil;
33 import org.codehaus.plexus.util.PropertyUtils;
34 import org.codehaus.plexus.util.StringUtils;
35 import org.codehaus.plexus.velocity.VelocityComponent;
36 import org.objectweb.asm.ClassReader;
37 import org.objectweb.asm.ClassVisitor;
38 import org.objectweb.asm.ClassWriter;
39 import org.objectweb.asm.commons.Remapper;
40 import org.objectweb.asm.commons.RemappingClassAdapter;
41 import org.objectweb.asm.commons.SimpleRemapper;
42
43 import java.io.File;
44 import java.io.FileInputStream;
45 import java.io.FileOutputStream;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.InputStreamReader;
49 import java.io.OutputStreamWriter;
50 import java.io.PrintWriter;
51 import java.io.Reader;
52 import java.io.StringWriter;
53 import java.io.UnsupportedEncodingException;
54 import java.util.List;
55 import java.util.Properties;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class PluginHelpGenerator
72 extends AbstractLogEnabled
73 implements Generator
74 {
75
76
77
78 private static final String HELP_MOJO_CLASS_NAME = "HelpMojo";
79
80
81
82
83 private static final String HELP_PROPERTIES_FILENAME = "maven-plugin-help.properties";
84
85
86
87
88 private static final String HELP_GOAL = "help";
89
90 private String helpPackageName;
91
92 private boolean useAnnotations;
93
94 private VelocityComponent velocityComponent;
95
96
97
98
99 public PluginHelpGenerator()
100 {
101 this.enableLogging( new ConsoleLogger( Logger.LEVEL_INFO, "PluginHelpGenerator" ) );
102 }
103
104
105
106
107
108
109
110
111 public void execute( File destinationDirectory, PluginToolsRequest request )
112 throws GeneratorException
113 {
114 PluginDescriptor pluginDescriptor = request.getPluginDescriptor();
115
116 String helpImplementation = getImplementation( pluginDescriptor );
117
118 @SuppressWarnings( "unchecked" )
119 List<MojoDescriptor> mojoDescriptors = pluginDescriptor.getMojos();
120
121 if ( mojoDescriptors != null )
122 {
123
124 MojoDescriptor descriptor = pluginDescriptor.getMojo( HELP_GOAL );
125
126 if ( ( descriptor != null ) && !descriptor.getImplementation().equals( helpImplementation ) )
127 {
128 if ( getLogger().isWarnEnabled() )
129 {
130 getLogger().warn( "\n\nA help goal (" + descriptor.getImplementation()
131 + ") already exists in this plugin. SKIPPED THE " + helpImplementation
132 + " GENERATION.\n" );
133 }
134
135 return;
136 }
137 }
138
139 writeHelpPropertiesFile( request, destinationDirectory );
140
141 useAnnotations =
142 request.getProject().getArtifactMap().containsKey(
143 "org.apache.maven.plugin-tools:maven-plugin-annotations" );
144
145 try
146 {
147 String sourcePath = helpImplementation.replace( '.', File.separatorChar ) + ".java";
148
149 File helpClass = new File( destinationDirectory, sourcePath );
150 helpClass.getParentFile().mkdirs();
151
152 String helpClassSources = getHelpClassSources( getPluginHelpPath( request.getProject() ),
153 pluginDescriptor );
154
155 FileUtils.fileWrite( helpClass, request.getEncoding(), helpClassSources );
156 }
157 catch ( IOException e )
158 {
159 throw new GeneratorException( e.getMessage(), e );
160 }
161 }
162
163 public PluginHelpGenerator setHelpPackageName( String helpPackageName )
164 {
165 this.helpPackageName = helpPackageName;
166 return this;
167 }
168
169 public VelocityComponent getVelocityComponent()
170 {
171 return velocityComponent;
172 }
173
174 public PluginHelpGenerator setVelocityComponent( VelocityComponent velocityComponent )
175 {
176 this.velocityComponent = velocityComponent;
177 return this;
178 }
179
180
181
182
183
184 private String getHelpClassSources( String pluginHelpPath, PluginDescriptor pluginDescriptor )
185 {
186 Properties properties = new Properties();
187 VelocityContext context = new VelocityContext( properties );
188 if ( this.helpPackageName != null )
189 {
190 properties.put( "helpPackageName", this.helpPackageName );
191 }
192 else
193 {
194 properties.put( "helpPackageName", "" );
195 }
196 properties.put( "pluginHelpPath", pluginHelpPath );
197 properties.put( "artifactId", pluginDescriptor.getArtifactId() );
198 properties.put( "goalPrefix", pluginDescriptor.getGoalPrefix() );
199 properties.put( "useAnnotations", useAnnotations );
200
201 StringWriter stringWriter = new StringWriter();
202
203 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream( "help-class-source.vm" );
204 InputStreamReader isReader = null;
205 try
206 {
207 isReader = new InputStreamReader( is, "UTF-8" );
208
209 velocityComponent.getEngine().evaluate( context, stringWriter, "", isReader );
210 }
211 catch ( UnsupportedEncodingException e )
212 {
213
214 }
215 finally
216 {
217 IOUtil.close( is );
218 IOUtil.close( isReader );
219 }
220
221 return stringWriter.toString();
222 }
223
224
225
226
227
228
229 private String getImplementation( PluginDescriptor pluginDescriptor )
230 {
231 if ( StringUtils.isEmpty( helpPackageName ) )
232 {
233 helpPackageName = GeneratorUtils.discoverPackageName( pluginDescriptor );
234 }
235
236 return StringUtils.isEmpty( helpPackageName ) ? HELP_MOJO_CLASS_NAME : helpPackageName + '.'
237 + HELP_MOJO_CLASS_NAME;
238 }
239
240
241
242
243
244
245
246
247 private void writeHelpPropertiesFile( PluginToolsRequest request, File destinationDirectory )
248 throws GeneratorException
249 {
250 Properties properties = new Properties();
251 properties.put( "helpPackageName", helpPackageName == null ? "" : helpPackageName );
252 properties.put( "destinationDirectory", destinationDirectory.getAbsolutePath() );
253
254 File tmpPropertiesFile =
255 new File( request.getProject().getBuild().getDirectory(), HELP_PROPERTIES_FILENAME );
256
257 if ( tmpPropertiesFile.exists() )
258 {
259 tmpPropertiesFile.delete();
260 }
261 else if ( !tmpPropertiesFile.getParentFile().exists() )
262 {
263 tmpPropertiesFile.getParentFile().mkdirs();
264 }
265
266 FileOutputStream fos = null;
267 try
268 {
269 fos = new FileOutputStream( tmpPropertiesFile );
270 properties.store( fos, "maven plugin help mojo generation informations" );
271 }
272 catch ( IOException e )
273 {
274 throw new GeneratorException( e.getMessage(), e );
275 }
276 finally
277 {
278 IOUtil.close( fos );
279 }
280 }
281
282 static String getPluginHelpPath( MavenProject mavenProject )
283 {
284 return "META-INF/maven/" + mavenProject.getGroupId() + "/" + mavenProject.getArtifactId() + "/plugin-help.xml";
285 }
286
287
288
289
290
291
292
293
294 static void rewriteHelpMojo( PluginToolsRequest request, Log log )
295 throws GeneratorException
296 {
297 File tmpPropertiesFile =
298 new File( request.getProject().getBuild().getDirectory(), HELP_PROPERTIES_FILENAME );
299
300 if ( !tmpPropertiesFile.exists() )
301 {
302 return;
303 }
304
305 Properties properties = PropertyUtils.loadProperties( tmpPropertiesFile );
306
307 String helpPackageName = properties.getProperty( "helpPackageName" );
308
309
310 if ( StringUtils.isEmpty( helpPackageName ) )
311 {
312 String destDir = properties.getProperty( "destinationDirectory" );
313 File destinationDirectory;
314 if ( StringUtils.isEmpty( destDir ) )
315 {
316
317 log.warn( "\n\nUnexpected situation: destinationDirectory not defined in " + HELP_PROPERTIES_FILENAME
318 + " during help mojo source generation but expected during XML descriptor generation." );
319 log.warn( "Please check helpmojo goal version used in previous build phase." );
320 log.warn( "If you just upgraded to plugin-tools >= 3.2 you must run a clean build at least once." );
321 destinationDirectory = new File( "target/generated-sources/plugin" );
322 log.warn( "Trying default location: " + destinationDirectory );
323 }
324 else
325 {
326 destinationDirectory = new File( destDir );
327 }
328 String helpMojoImplementation = rewriteHelpClassToMojoPackage( request, destinationDirectory, log );
329
330 if ( helpMojoImplementation != null )
331 {
332
333 updateHelpMojoDescriptor( request.getPluginDescriptor(), helpMojoImplementation );
334 }
335 }
336 }
337
338 private static String rewriteHelpClassToMojoPackage( PluginToolsRequest request, File destinationDirectory,
339 Log log )
340 throws GeneratorException
341 {
342 String destinationPackage = GeneratorUtils.discoverPackageName( request.getPluginDescriptor() );
343 if ( StringUtils.isEmpty( destinationPackage ) )
344 {
345 return null;
346 }
347 String packageAsDirectory = StringUtils.replace( destinationPackage, '.', '/' );
348
349 String outputDirectory = request.getProject().getBuild().getOutputDirectory();
350 File helpClassFile = new File( outputDirectory, HELP_MOJO_CLASS_NAME + ".class" );
351 if ( !helpClassFile.exists() )
352 {
353 return null;
354 }
355
356
357 File helpSourceFile = new File( destinationDirectory, HELP_MOJO_CLASS_NAME + ".java" );
358 if ( !helpSourceFile.exists() )
359 {
360 log.warn( "HelpMojo.java not found in default location: " + helpSourceFile.getAbsolutePath() );
361 log.warn( "Help goal source won't be moved to package: " + destinationPackage );
362 }
363 else
364 {
365 File helpSourceFileNew =
366 new File( destinationDirectory, packageAsDirectory + '/' + HELP_MOJO_CLASS_NAME + ".java" );
367 if ( !helpSourceFileNew.getParentFile().exists() )
368 {
369 helpSourceFileNew.getParentFile().mkdirs();
370 }
371 Reader sourceReader = null;
372 PrintWriter sourceWriter = null;
373 try
374 {
375 sourceReader = new InputStreamReader( new FileInputStream( helpSourceFile ), request.getEncoding() );
376 sourceWriter =
377 new PrintWriter( new OutputStreamWriter( new FileOutputStream( helpSourceFileNew ),
378 request.getEncoding() ) );
379
380 sourceWriter.println( "package " + destinationPackage + ";" );
381 IOUtil.copy( sourceReader, sourceWriter );
382 }
383 catch ( IOException e )
384 {
385 throw new GeneratorException( e.getMessage(), e );
386 }
387 finally
388 {
389 IOUtil.close( sourceReader );
390 IOUtil.close( sourceWriter );
391 }
392 helpSourceFileNew.setLastModified( helpSourceFile.lastModified() );
393 helpSourceFile.delete();
394 }
395
396
397 File rewriteHelpClassFile =
398 new File( outputDirectory + '/' + packageAsDirectory, HELP_MOJO_CLASS_NAME + ".class" );
399 if ( !rewriteHelpClassFile.getParentFile().exists() )
400 {
401 rewriteHelpClassFile.getParentFile().mkdirs();
402 }
403
404 FileInputStream fileInputStream = null;
405 ClassReader cr = null;
406 try
407 {
408 fileInputStream = new FileInputStream( helpClassFile );
409 cr = new ClassReader( fileInputStream );
410 }
411 catch ( IOException e )
412 {
413 throw new GeneratorException( e.getMessage(), e );
414 }
415 finally
416 {
417 IOUtil.close( fileInputStream );
418 }
419
420 ClassWriter cw = new ClassWriter( 0 );
421
422 Remapper packageRemapper =
423 new SimpleRemapper( HELP_MOJO_CLASS_NAME, packageAsDirectory + '/' + HELP_MOJO_CLASS_NAME );
424 ClassVisitor cv = new RemappingClassAdapter( cw, packageRemapper );
425
426 try
427 {
428 cr.accept( cv, ClassReader.EXPAND_FRAMES );
429 }
430 catch ( Throwable e )
431 {
432 throw new GeneratorException( "ASM issue processing class-file " + helpClassFile.getPath(), e );
433 }
434
435 byte[] renamedClass = cw.toByteArray();
436 FileOutputStream fos = null;
437 try
438 {
439 fos = new FileOutputStream( rewriteHelpClassFile );
440 fos.write( renamedClass );
441 }
442 catch ( IOException e )
443 {
444 throw new GeneratorException( "Error rewriting help class: " + e.getMessage(), e );
445 }
446 finally
447 {
448 IOUtil.close( fos );
449 }
450
451 helpClassFile.delete();
452
453 return destinationPackage + ".HelpMojo";
454 }
455
456 private static void updateHelpMojoDescriptor( PluginDescriptor pluginDescriptor, String helpMojoImplementation )
457 {
458 MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( HELP_GOAL );
459
460 if ( mojoDescriptor != null )
461 {
462 mojoDescriptor.setImplementation( helpMojoImplementation );
463 }
464 }
465 }