1 package org.codehaus.mojo.sqlj;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.PrintWriter;
6 import java.io.StringWriter;
7 import java.net.URL;
8 import java.net.URLClassLoader;
9 import java.util.ArrayList;
10 import java.util.Arrays;
11 import java.util.HashSet;
12 import java.util.List;
13 import java.util.Set;
14
15 import org.apache.commons.beanutils.MethodUtils;
16 import org.apache.commons.io.FileUtils;
17 import org.apache.commons.lang.StringUtils;
18 import org.apache.commons.lang.SystemUtils;
19 import org.apache.maven.model.Resource;
20 import org.apache.maven.plugin.MojoExecutionException;
21 import org.apache.maven.plugin.MojoFailureException;
22 import org.apache.maven.plugins.annotations.Component;
23 import org.apache.maven.plugins.annotations.LifecyclePhase;
24 import org.apache.maven.plugins.annotations.Mojo;
25 import org.apache.maven.plugins.annotations.Parameter;
26 import org.apache.maven.plugins.annotations.ResolutionScope;
27 import org.apache.maven.project.MavenProject;
28 import org.codehaus.plexus.util.Scanner;
29 import org.sonatype.plexus.build.incremental.BuildContext;
30
31
32
33
34
35
36 @Mojo( name = "sqlj", defaultPhase = LifecyclePhase.GENERATE_SOURCES, requiresDependencyResolution = ResolutionScope.COMPILE )
37 public class SqljMojo
38 extends AbstractSqljMojo
39 {
40
41 private static final String SQLJ_CLASS = "sqlj.tools.Sqlj";
42
43
44
45
46 @Parameter( property = "sqlj.encoding", defaultValue = "${project.build.sourceEncoding}" )
47 private String encoding;
48
49
50
51
52
53
54 @Parameter( property = "sqlj.verbose", defaultValue = "false" )
55 private boolean verbose;
56
57
58
59
60 @Parameter( property = "sqlj.sqljFiles" )
61 private File[] sqljFiles;
62
63
64
65
66 @Parameter( property = "sqlj.sqljDirectories" )
67 private File[] sqljDirs;
68
69
70
71
72 @Parameter ( property = "sqlj.additionalArgs")
73 private List<String> additionalArgs;
74
75
76
77
78 @Component
79 private MavenProject mavenProject;
80
81
82
83
84 @Component
85 private BuildContext buildContext;
86
87
88
89
90 public void execute()
91 throws MojoExecutionException, MojoFailureException
92 {
93 URLClassLoader sqljClassLoader = getSqljClassLoader();
94 if ( getLog().isDebugEnabled() )
95 {
96 getLog().debug( "SQLJ classpath:" );
97 for ( URL url : sqljClassLoader.getURLs() )
98 {
99 getLog().debug( " " + url.getPath() );
100 }
101 }
102
103 String sqljVersionInfo = getSqljVersionInfo( sqljClassLoader );
104 if ( sqljVersionInfo != null )
105 {
106 getLog().info( "Using SQLJ Translator version '" + sqljVersionInfo + "'" );
107 }
108 else
109 {
110 getLog().info( "Couldn't retrieve SQLJ Translator version info" );
111 }
112
113 if ( !checkSqljDirAndFileDeclarations() )
114 {
115 String msg = "Plugin configuration contains invalid SQLJ directory or file declaration(s).";
116 throw new MojoExecutionException( msg );
117 }
118
119 if ( StringUtils.isEmpty( encoding ) )
120 {
121 encoding = SystemUtils.FILE_ENCODING;
122 getLog().warn( "No encoding given, falling back to system default value: " + encoding );
123 }
124
125 try
126 {
127 FileUtils.forceMkdir( getGeneratedResourcesDirectory().getAbsoluteFile() );
128 FileUtils.forceMkdir( getGeneratedSourcesDirectory().getAbsoluteFile() );
129 }
130 catch ( IOException e )
131 {
132 throw new MojoFailureException( e.getMessage() );
133 }
134
135 Set<File> staleSqljFiles = getStaleSqljFiles();
136 boolean translationPerformed;
137 if ( !staleSqljFiles.isEmpty() )
138 {
139 for ( File file : staleSqljFiles )
140 {
141 buildContext.removeMessages( file );
142 try
143 {
144 translate( file, sqljClassLoader );
145 }
146 catch ( MojoExecutionException e )
147 {
148 final String msg = "Error translating SQLJ file";
149 buildContext.addMessage( file, 0, 0, msg, BuildContext.SEVERITY_ERROR, e );
150 throw e;
151 }
152 }
153 translationPerformed = true;
154 final int numberOfFiles = staleSqljFiles.size();
155 getLog().info( "Translated " + numberOfFiles + " SQLJ file" + ( numberOfFiles > 0 ? "s." : "." ) );
156 }
157 else
158 {
159 getLog().info( "No updated SQLJ files found - skipping SQLJ translation." );
160 translationPerformed = false;
161 }
162
163 Resource resource = new Resource();
164 resource.setDirectory( getGeneratedResourcesDirectory().getAbsolutePath() );
165 mavenProject.addResource( resource );
166 if ( translationPerformed )
167 {
168 buildContext.refresh( getGeneratedResourcesDirectory() );
169 }
170 mavenProject.addCompileSourceRoot( getGeneratedSourcesDirectory().getAbsolutePath() );
171 if ( translationPerformed )
172 {
173 buildContext.refresh( getGeneratedSourcesDirectory() );
174 }
175 }
176
177 private List<URL> getProjectClasspath()
178 throws Exception
179 {
180 @SuppressWarnings( "unchecked" )
181 final List<String> classpathElements = mavenProject.getCompileClasspathElements();
182 List<URL> result = new ArrayList<URL>( classpathElements.size() );
183 for ( String s : classpathElements )
184 {
185 File tmp = new File( s );
186 result.add( tmp.toURI().toURL() );
187 }
188
189 return result;
190 }
191
192 private URLClassLoader getSqljClassLoader()
193 throws MojoExecutionException, MojoFailureException
194 {
195 ClassLoader pluginClassLoader = Thread.currentThread().getContextClassLoader();
196 if ( pluginClassLoader instanceof URLClassLoader == false )
197 {
198 throw new MojoFailureException( "Unexpected error: Class loader is not of URLClassLoader type" );
199 }
200 URLClassLoader pluginURLClassLoader = (URLClassLoader) pluginClassLoader;
201
202
203
204
205 URL[] pluginClassLoaderURLs = pluginURLClassLoader.getURLs();
206 List<URL> projectClasspathURLs;
207 try
208 {
209 projectClasspathURLs = getProjectClasspath();
210 }
211 catch ( Exception e )
212 {
213 throw new MojoExecutionException( "Couldn't retrieve classpath for project", e );
214 }
215
216 List<URL> tmp = new ArrayList<URL>( pluginClassLoaderURLs.length + projectClasspathURLs.size() );
217 for ( URL url : pluginClassLoaderURLs )
218 {
219 tmp.add( url );
220 }
221 tmp.addAll( projectClasspathURLs );
222
223 URLClassLoader newClassLoader = new URLClassLoader( tmp.toArray( new URL[0] ) );
224 return newClassLoader;
225 }
226
227 private boolean checkSqljDirAndFileDeclarations()
228 {
229 boolean isOk = true;
230
231 for ( File sqljDir : sqljDirs )
232 {
233 if ( !sqljDir.exists() )
234 {
235 getLog().error( "Declared SQLJ directory does not exist: " + sqljDir );
236 isOk = false;
237 }
238 if ( !sqljDir.isDirectory() )
239 {
240 getLog().error( "Declared SQLJ directory is not a directory: " + sqljDir );
241 isOk = false;
242 }
243 }
244 for ( File sqljFile : sqljFiles )
245 {
246 if ( !sqljFile.exists() )
247 {
248 getLog().error( "Declared SQLJ file does not exist: " + sqljFile );
249 isOk = false;
250 }
251 if ( !sqljFile.isFile() )
252 {
253 getLog().error( "Declared SQLJ file is not a file: " + sqljFile );
254 isOk = false;
255 }
256 }
257
258 return isOk;
259 }
260
261
262
263
264
265
266
267
268
269 private void translate( File file, ClassLoader classLoader )
270 throws MojoFailureException, MojoExecutionException
271 {
272 Class<?> sqljClass;
273 try
274 {
275 sqljClass = classLoader.loadClass( SQLJ_CLASS );
276 }
277 catch ( ClassNotFoundException e )
278 {
279 throw new MojoFailureException( "Please add SQLJ Translator to the plugin's classpath: " + e.getMessage() );
280 }
281 catch ( Exception e )
282 {
283 throw new MojoFailureException( e.getMessage() );
284 }
285
286 List<String> sqljArgs = new ArrayList<String>();
287 sqljArgs.add( "-dir=" + getGeneratedSourcesDirectory().getAbsolutePath() );
288 sqljArgs.add( "-d=" + getGeneratedResourcesDirectory().getAbsolutePath() );
289 sqljArgs.add( "-encoding=" + encoding );
290 if ( verbose || getLog().isDebugEnabled() )
291 {
292 sqljArgs.add( "-status" );
293 }
294 sqljArgs.add( "-compile=false" );
295 sqljArgs.add( file.getAbsolutePath() );
296 sqljArgs.addAll(additionalArgs);
297
298 Integer returnCode = null;
299 try
300 {
301 if ( getLog().isDebugEnabled() )
302 {
303 getLog().debug( "Performing SQLJ translation on " + file );
304 }
305 Object[] methodArgs = new Object[] { sqljArgs.toArray( new String[0] ) };
306 returnCode = (Integer) MethodUtils.invokeExactStaticMethod( sqljClass, "statusMain", methodArgs );
307 }
308 catch ( Exception e )
309 {
310 throw new MojoFailureException( e.getMessage() );
311 }
312
313 if ( returnCode.intValue() != 0 )
314 {
315 throw new MojoExecutionException( "Can't translate file (return code: " + returnCode + ")" );
316 }
317 }
318
319
320
321
322
323 private Set<File> getStaleSqljFiles()
324 throws MojoExecutionException
325 {
326 Set<File> staleFiles = new HashSet<File>();
327
328
329 final String[] sqljIncludes = new String[] { "**/*.sqlj" };
330 for ( File sqljDir : sqljDirs )
331 {
332 if ( getLog().isDebugEnabled() )
333 {
334 getLog().debug( "Checking for updated SQLJ files in directory: " + sqljDir );
335 }
336 Scanner modifiedScanner = this.buildContext.newScanner( sqljDir );
337 modifiedScanner.setIncludes( sqljIncludes );
338 modifiedScanner.setExcludes( null );
339 modifiedScanner.scan();
340
341 String[] modifiedFiles = modifiedScanner.getIncludedFiles();
342 for ( String path : modifiedFiles )
343 {
344 File file = new File( sqljDir, path );
345 if ( getLog().isDebugEnabled() )
346 {
347 getLog().debug( "Updated SQLJ file found: " + file );
348 }
349 if ( !staleFiles.add( file ) )
350 {
351 getLog().warn( "Duplicated declaration of SQLJ source detected in plugin configuration: " + file );
352 }
353 }
354 }
355
356
357 for ( File sqljFile : sqljFiles )
358 {
359 if ( getLog().isDebugEnabled() )
360 {
361 getLog().debug( "Checking if SQLJ file has been updated: " + sqljFile );
362 }
363 Scanner modifiedScanner = this.buildContext.newScanner( sqljFile.getParentFile() );
364 modifiedScanner.setIncludes( new String[] { sqljFile.getName() } );
365 modifiedScanner.setExcludes( null );
366 modifiedScanner.scan();
367
368 String[] modifiedFiles = modifiedScanner.getIncludedFiles();
369 if ( modifiedFiles.length == 1 )
370 {
371 String path = modifiedFiles[0];
372 File file = new File( sqljFile.getParentFile(), path );
373 if ( file.compareTo( sqljFile ) == 0 )
374 {
375 if ( getLog().isDebugEnabled() )
376 {
377 getLog().debug( "Updated SQLJ file found: " + sqljFile );
378 }
379 if ( !staleFiles.add( file ) )
380 {
381 getLog().warn( "Duplicated declaration of SQLJ source detected in plugin configuration: "
382 + file );
383 }
384 }
385 else
386 {
387
388 getLog().error( "Unexpected SQLJ file returned; aborting..." );
389 throw new MojoExecutionException( "Unexpected SQLJ file returned when examining " + sqljFile + ": "
390 + file );
391 }
392 }
393 else if ( modifiedFiles.length > 1 )
394 {
395
396 getLog().error( "Unexpected list of SQLJ files returned; aborting..." );
397 throw new MojoExecutionException( "Unexpected list of SQLJ files returned when examining " + sqljFile
398 + ": " + Arrays.toString( modifiedFiles ) );
399 }
400 }
401
402
403
404
405 return staleFiles;
406 }
407
408 private String getSqljVersionInfo( ClassLoader classLoader )
409 {
410 try
411 {
412 Class<?> sqljClass = classLoader.loadClass( SQLJ_CLASS );
413 String version = null;
414 try
415 {
416 version = (String) MethodUtils.invokeExactStaticMethod( sqljClass, "getVersion", null );
417 }
418 catch ( NoSuchMethodException e )
419 {
420 StringWriter stringWriter = new StringWriter();
421 MethodUtils.invokeExactStaticMethod( sqljClass, "printVersion", new PrintWriter( stringWriter ) );
422 version = stringWriter.toString();
423 }
424 return version;
425 }
426 catch ( Exception e )
427 {
428 return null;
429 }
430 }
431
432 }