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