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.project.MavenProject;
25 import org.apache.maven.tools.plugin.PluginToolsRequest;
26 import org.apache.velocity.VelocityContext;
27 import org.codehaus.plexus.logging.AbstractLogEnabled;
28 import org.codehaus.plexus.logging.Logger;
29 import org.codehaus.plexus.logging.console.ConsoleLogger;
30 import org.codehaus.plexus.util.FileUtils;
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.velocity.VelocityComponent;
35 import org.objectweb.asm.ClassReader;
36 import org.objectweb.asm.ClassVisitor;
37 import org.objectweb.asm.ClassWriter;
38 import org.objectweb.asm.commons.Remapper;
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.InputStream;
47 import java.io.InputStreamReader;
48 import java.io.StringWriter;
49 import java.util.List;
50 import java.util.Properties;
51
52
53
54
55
56
57
58
59 public class PluginHelpGenerator
60 extends AbstractLogEnabled
61 implements Generator
62 {
63
64
65
66 private static final String HELP_MOJO_CLASS_NAME = "HelpMojo";
67
68
69
70
71 private static final String HELP_PROPERTIES_FILENAME = "maven-plugin-help.properties";
72
73
74
75
76 private static final String HELP_GOAL = "help";
77
78 private String helpPackageName;
79
80 private VelocityComponent velocityComponent;
81
82
83
84
85 public PluginHelpGenerator()
86 {
87 this.enableLogging( new ConsoleLogger( Logger.LEVEL_INFO, "PluginHelpGenerator" ) );
88 }
89
90
91
92
93
94
95
96
97 public void execute( File destinationDirectory, PluginToolsRequest request )
98 throws GeneratorException
99 {
100 PluginDescriptor pluginDescriptor = request.getPluginDescriptor();
101
102 String helpImplementation = getImplementation( pluginDescriptor );
103
104 @SuppressWarnings( "unchecked" )
105 List<MojoDescriptor> mojoDescriptors = pluginDescriptor.getMojos();
106
107 if ( mojoDescriptors != null )
108 {
109
110 MojoDescriptor descriptor = pluginDescriptor.getMojo( HELP_GOAL );
111
112 if ( ( descriptor != null ) && !descriptor.getImplementation().equals( helpImplementation ) )
113 {
114 if ( getLogger().isWarnEnabled() )
115 {
116 getLogger().warn( "\n\nA help goal (" + descriptor.getImplementation()
117 + ") already exists in this plugin. SKIPPED THE " + helpImplementation
118 + " GENERATION.\n" );
119 }
120
121 return;
122 }
123 }
124
125 writeHelpPropertiesFile( request );
126
127 try
128 {
129 String sourcePath = helpImplementation.replace( '.', File.separatorChar ) + ".java";
130
131 File helpClass = new File( destinationDirectory, sourcePath );
132 helpClass.getParentFile().mkdirs();
133
134 MavenProject mavenProject = request.getProject();
135 String pluginResourcesPath = "META-INF/maven/" + mavenProject.getGroupId() + "/" + mavenProject.getArtifactId();
136
137 String helpClassSources = getHelpClassSources( pluginResourcesPath, pluginDescriptor );
138
139 FileUtils.fileWrite( helpClass, request.getEncoding(), helpClassSources );
140 }
141 catch ( IOException e )
142 {
143 throw new GeneratorException( e.getMessage(), e );
144 }
145 }
146
147 public PluginHelpGenerator setHelpPackageName( String helpPackageName )
148 {
149 this.helpPackageName = helpPackageName;
150 return this;
151 }
152
153 public VelocityComponent getVelocityComponent()
154 {
155 return velocityComponent;
156 }
157
158 public PluginHelpGenerator setVelocityComponent( VelocityComponent velocityComponent )
159 {
160 this.velocityComponent = velocityComponent;
161 return this;
162 }
163
164
165
166
167
168 private String getHelpClassSources( String pluginResourcesPath, PluginDescriptor pluginDescriptor )
169 {
170 Properties properties = new Properties();
171 VelocityContext context = new VelocityContext( properties );
172 if ( this.helpPackageName != null )
173 {
174 properties.put( "helpPackageName", this.helpPackageName );
175 }
176 else
177 {
178 properties.put( "helpPackageName", "" );
179 }
180 properties.put( "pluginHelpPath", pluginResourcesPath + "/plugin-help.xml" );
181 properties.put( "artifactId", pluginDescriptor.getArtifactId() );
182 properties.put( "goalPrefix", pluginDescriptor.getGoalPrefix() );
183
184
185
186 StringWriter stringWriter = new StringWriter();
187
188 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream( "help-class-source.vm" );
189 InputStreamReader isReader = new InputStreamReader( is );
190 velocityComponent.getEngine().evaluate( context, stringWriter, "", isReader );
191
192 return stringWriter.toString();
193 }
194
195
196
197
198
199
200 private String getImplementation( PluginDescriptor pluginDescriptor )
201 {
202 String packageName = helpPackageName;
203 if ( StringUtils.isEmpty( packageName ) )
204 {
205 packageName = GeneratorUtils.discoverPackageName( pluginDescriptor );
206 }
207
208 return StringUtils.isEmpty( packageName ) ? HELP_MOJO_CLASS_NAME : packageName + '.' + HELP_MOJO_CLASS_NAME;
209 }
210
211
212
213
214
215
216
217
218 private void writeHelpPropertiesFile( PluginToolsRequest request )
219 throws GeneratorException
220 {
221 Properties properties = new Properties();
222 properties.put( "helpPackageName", helpPackageName == null ? "" : helpPackageName );
223
224 File tmpPropertiesFile =
225 new File( request.getProject().getBuild().getDirectory(), HELP_PROPERTIES_FILENAME );
226
227 if ( tmpPropertiesFile.exists() )
228 {
229 tmpPropertiesFile.delete();
230 }
231 else if ( !tmpPropertiesFile.getParentFile().exists() )
232 {
233 tmpPropertiesFile.getParentFile().mkdirs();
234 }
235
236 FileOutputStream fos = null;
237 try
238 {
239 fos = new FileOutputStream( tmpPropertiesFile );
240 properties.store( fos, "maven plugin help mojo generation informations" );
241 }
242 catch ( IOException e )
243 {
244 throw new GeneratorException( e.getMessage(), e );
245 }
246 finally
247 {
248 IOUtil.close( fos );
249 }
250 }
251
252
253
254
255
256
257
258
259 static void rewriteHelpMojo( PluginToolsRequest request )
260 throws GeneratorException
261 {
262 File tmpPropertiesFile =
263 new File( request.getProject().getBuild().getDirectory(), HELP_PROPERTIES_FILENAME );
264
265 if ( !tmpPropertiesFile.exists() )
266 {
267 return;
268 }
269
270 Properties properties = PropertyUtils.loadProperties( tmpPropertiesFile );
271
272 String helpPackageName = properties.getProperty( "helpPackageName" );
273
274
275 if ( StringUtils.isEmpty( helpPackageName ) )
276 {
277 String helpMojoImplementation = rewriteHelpClassToMojoPackage( request );
278
279 if ( helpMojoImplementation != null )
280 {
281
282 updateHelpMojoDescriptor( request.getPluginDescriptor(), helpMojoImplementation );
283 }
284 }
285 }
286
287 private static String rewriteHelpClassToMojoPackage( PluginToolsRequest request )
288 throws GeneratorException
289 {
290 String destinationPackage = GeneratorUtils.discoverPackageName( request.getPluginDescriptor() );
291 if ( StringUtils.isEmpty( destinationPackage ) )
292 {
293 return null;
294 }
295 String packageAsDirectory = StringUtils.replace( destinationPackage, '.', '/' );
296
297 String outputDirectory = request.getProject().getBuild().getOutputDirectory();
298 File helpClassFile = new File( outputDirectory, HELP_MOJO_CLASS_NAME + ".class" );
299 if ( !helpClassFile.exists() )
300 {
301 return null;
302 }
303
304 File rewriteHelpClassFile =
305 new File( outputDirectory + '/' + packageAsDirectory, HELP_MOJO_CLASS_NAME + ".class" );
306 if ( !rewriteHelpClassFile.getParentFile().exists() )
307 {
308 rewriteHelpClassFile.getParentFile().mkdirs();
309 }
310
311 FileInputStream fileInputStream = null;
312 ClassReader cr = null;
313 try
314 {
315 fileInputStream = new FileInputStream( helpClassFile );
316 cr = new ClassReader( fileInputStream );
317 }
318 catch ( IOException e )
319 {
320 throw new GeneratorException( e.getMessage(), e );
321 }
322 finally
323 {
324 IOUtil.close( fileInputStream );
325 }
326
327 ClassWriter cw = new ClassWriter( 0 );
328
329 Remapper packageRemapper =
330 new SimpleRemapper( HELP_MOJO_CLASS_NAME, packageAsDirectory + '/' + HELP_MOJO_CLASS_NAME );
331 ClassVisitor cv = new RemappingClassAdapter( cw, packageRemapper );
332
333 try
334 {
335 cr.accept( cv, ClassReader.EXPAND_FRAMES );
336 }
337 catch ( Throwable e )
338 {
339 throw new GeneratorException( "ASM issue processing class-file " + helpClassFile.getPath(), e );
340 }
341
342 byte[] renamedClass = cw.toByteArray();
343 FileOutputStream fos = null;
344 try
345 {
346 fos = new FileOutputStream( rewriteHelpClassFile );
347 fos.write( renamedClass );
348 }
349 catch ( IOException e )
350 {
351 throw new GeneratorException( "Error rewriting help class: " + e.getMessage(), e );
352 }
353 finally
354 {
355 IOUtil.close( fos );
356 }
357
358 helpClassFile.delete();
359
360 return destinationPackage + ".HelpMojo";
361 }
362
363 private static void updateHelpMojoDescriptor( PluginDescriptor pluginDescriptor, String helpMojoImplementation )
364 {
365 MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( HELP_GOAL );
366
367 if ( mojoDescriptor != null )
368 {
369 mojoDescriptor.setImplementation( helpMojoImplementation );
370 }
371 }
372 }