View Javadoc

1   /*
2    $Id: Groovy.java,v 1.10 2006/03/11 16:42:59 galleon Exp $
3   
4    Copyright 2005 (C) Jeremy Rayner. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  
47  package org.codehaus.groovy.ant;
48  
49  import groovy.lang.GroovyShell;
50  import groovy.lang.Script;
51  import groovy.lang.Binding;
52  import groovy.util.AntBuilder;
53  
54  import java.io.BufferedOutputStream;
55  import java.io.BufferedReader;
56  import java.io.File;
57  import java.io.FileOutputStream;
58  import java.io.FileReader;
59  import java.io.IOException;
60  import java.io.PrintStream;
61  import java.io.Reader;
62  import java.io.StringWriter;
63  import java.io.PrintWriter;
64  import java.lang.reflect.Field;
65  import java.util.Hashtable;
66  import java.util.Vector;
67  
68  import org.apache.tools.ant.BuildException;
69  import org.apache.tools.ant.DirectoryScanner;
70  import org.apache.tools.ant.Project;
71  import org.apache.tools.ant.Task;
72  import org.apache.tools.ant.types.FileSet;
73  import org.apache.tools.ant.types.Path;
74  import org.apache.tools.ant.types.Reference;
75  import org.codehaus.groovy.control.CompilationFailedException;
76  import org.codehaus.groovy.control.CompilerConfiguration;
77  import org.codehaus.groovy.runtime.InvokerHelper;
78  import org.codehaus.groovy.tools.ErrorReporter;
79  
80  /***
81   * Executes a series of Groovy statements.
82   *
83   * <p>Statements can
84   * either be read in from a text file using the <i>src</i> attribute or from
85   * between the enclosing groovy tags.</p>
86   *
87   *
88   * Based heavily on SQLExec.java which is part of apache-ant
89   * http://cvs.apache.org/viewcvs.cgi/ant/src/main/org/apache/tools/ant/taskdefs/SQLExec.java?rev=MAIN
90   *
91   * Copyright  2000-2005 The Apache Software Foundation
92   *
93   *  Licensed under the Apache License, Version 2.0 (the "License");
94   *  you may not use this file except in compliance with the License.
95   *  You may obtain a copy of the License at
96   *
97   *      http://www.apache.org/licenses/LICENSE-2.0
98   *
99   *  Unless required by applicable law or agreed to in writing, software
100  *  distributed under the License is distributed on an "AS IS" BASIS,
101  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
102  *  See the License for the specific language governing permissions and
103  *  limitations under the License.
104  *
105  *
106  */
107 public class Groovy extends Task {
108     /***
109      * files to load
110      */
111     private Vector filesets = new Vector();
112 
113     /***
114      * input file
115      */
116     private File srcFile = null;
117 
118     /***
119      * input command
120      */
121     private String command = "";
122 
123     /***
124      * Print results.
125      */
126     private boolean print = false;
127 
128     /***
129      * Results Output file.
130      */
131     private File output = null;
132 
133     /***
134      * Append to an existing file or overwrite it?
135      */
136     private boolean append = false;
137 
138     /***
139      * Used for caching loaders / driver. This is to avoid
140      * getting an OutOfMemoryError when calling this task
141      * multiple times in a row.
142      */
143     private static Hashtable loaderMap = new Hashtable(3);
144 
145     private Path classpath;
146 
147     /***
148      * User name.
149      */
150     private String userId = null;
151 
152     /***
153      * Groovy Version needed for this collection of statements.
154      **/
155     private String version = null;
156 
157     /***
158      * Compiler configuration.
159      *
160      * Used to specify the debug output to print stacktraces in case something fails.
161      * TODO: Could probably be reused to specify the encoding of the files to load or other properties.
162      */
163     private CompilerConfiguration configuration = new CompilerConfiguration();
164 
165     /***
166      * Enable compiler to report stack trace information if a problem occurs
167      * during compilation.
168      * @param stacktrace
169      */
170     public void setStacktrace(boolean stacktrace) {
171         configuration.setDebug(stacktrace);
172     }
173 
174 
175     /***
176      * Set the name of the file to be run.
177      * Required unless statements are enclosed in the build file
178      */
179     public void setSrc(File srcFile) {
180         this.srcFile = srcFile;
181     }
182 
183     /***
184      * Set an inline command to execute.
185      * NB: Properties are not expanded in this text.
186      */
187     public void addText(String txt) {
188         log("addText('"+txt+"')", Project.MSG_VERBOSE);
189         this.command += txt;
190     }
191 
192     /***
193      * Adds a set of files (nested fileset attribute).
194      */
195     public void addFileset(FileSet set) {
196         filesets.addElement(set);
197     }
198 
199     /***
200      * Print results from the statements;
201      * optional, default false
202      */
203     public void setPrint(boolean print) {
204         this.print = print;
205     }
206 
207     /***
208      * Set the output file;
209      * optional, defaults to the Ant log.
210      */
211     public void setOutput(File output) {
212         this.output = output;
213     }
214 
215     /***
216      * whether output should be appended to or overwrite
217      * an existing file.  Defaults to false.
218      *
219      * @since Ant 1.5
220      */
221     public void setAppend(boolean append) {
222         this.append = append;
223     }
224 
225 
226     /***
227      * Sets the classpath for loading.
228      * @param classpath The classpath to set
229      */
230     public void setClasspath(Path classpath) {
231         this.classpath = classpath;
232     }
233 
234     /***
235      * Add a path to the classpath for loading.
236      */
237     public Path createClasspath() {
238         if (this.classpath == null) {
239             this.classpath = new Path(getProject());
240         }
241         return this.classpath.createPath();
242     }
243 
244     /***
245      * Set the classpath for loading
246      * using the classpath reference.
247      */
248     public void setClasspathRef(Reference r) {
249         createClasspath().setRefid(r);
250     }
251 
252     /***
253      * Sets the version string, execute task only if
254      * groovy version match; optional.
255      * @param version The version to set
256      */
257     public void setVersion(String version) {
258         this.version = version;
259     }
260 
261 
262     protected static Hashtable getLoaderMap() {
263         return loaderMap;
264     }
265 
266 
267 
268 
269     /***
270      * Gets the classpath.
271      * @return Returns a Path
272      */
273     public Path getClasspath() {
274         return classpath;
275     }
276 
277     /***
278      * Gets the userId.
279      * @return Returns a String
280      */
281     public String getUserId() {
282         return userId;
283     }
284 
285     /***
286      * Set the user name for the connection; required.
287      * @param userId The userId to set
288      */
289     public void setUserid(String userId) {
290         this.userId = userId;
291     }
292 
293     /***
294      * Gets the version.
295      * @return Returns a String
296      */
297     public String getVersion() {
298         return version;
299     }
300 
301     /***
302      * Load the file and then execute it
303      */
304     public void execute() throws BuildException {
305         log("execute()", Project.MSG_VERBOSE);
306 
307         command = command.trim();
308 
309         try {
310             if (srcFile == null && command.length() == 0
311                 && filesets.isEmpty()) {
312                 throw new BuildException("Source file does not exist!", getLocation());
313             }
314 
315             if (srcFile != null && !srcFile.exists()) {
316                 throw new BuildException("Source file does not exist!", getLocation());
317             }
318 
319             // deal with the filesets
320             for (int i = 0; i < filesets.size(); i++) {
321                 FileSet fs = (FileSet) filesets.elementAt(i);
322                 DirectoryScanner ds = fs.getDirectoryScanner(getProject());
323                 File srcDir = fs.getDir(getProject());
324 
325                 String[] srcFiles = ds.getIncludedFiles();
326             }
327 
328             try {
329                 PrintStream out = System.out;
330                 try {
331                     if (output != null) {
332                         log("Opening PrintStream to output file " + output,
333                             Project.MSG_VERBOSE);
334                         out = new PrintStream(
335                                   new BufferedOutputStream(
336                                       new FileOutputStream(output
337                                                            .getAbsolutePath(),
338                                                            append)));
339                     }
340 
341                     // if there are no groovy statements between the enclosing Groovy tags
342                     // then read groovy statements in from a text file using the src attribute
343                     if (command == null || command.trim().length() == 0) {
344                         command = getText(new BufferedReader(new FileReader(srcFile)));
345                     }
346 
347 
348                     if (command != null) {
349                         execGroovy(command,out);
350                     } else {
351                         throw new BuildException("Source file does not exist!", getLocation());
352                     }
353 
354                 } finally {
355                     if (out != null && out != System.out) {
356                         out.close();
357                     }
358                 }
359             } catch (IOException e) {
360                 throw new BuildException(e, getLocation());
361             }
362 
363             log("statements executed successfully", Project.MSG_VERBOSE);
364         } finally{}
365     }
366 
367 
368     private static String getText(BufferedReader reader) throws IOException {
369         StringBuffer answer = new StringBuffer();
370         // reading the content of the file within a char buffer allow to keep the correct line endings
371         char[] charBuffer = new char[4096];
372         int nbCharRead = 0;
373         while ((nbCharRead = reader.read(charBuffer)) != -1) {
374             // appends buffer
375             answer.append(charBuffer, 0, nbCharRead);
376         }
377         reader.close();
378         return answer.toString();
379     }
380 
381 
382     /***
383      * read in lines and execute them
384      */
385     protected void runStatements(Reader reader, PrintStream out)
386         throws IOException {
387         log("runStatements()", Project.MSG_VERBOSE);
388 
389         StringBuffer txt = new StringBuffer();
390         String line = "";
391 
392         BufferedReader in = new BufferedReader(reader);
393 
394         while ((line = in.readLine()) != null) {
395             line = getProject().replaceProperties(line);
396 
397             if (line.indexOf("--") >= 0) {
398                 txt.append("\n");
399             }
400         }
401         // Catch any statements not followed by ;
402         if (!txt.equals("")) {
403             execGroovy(txt.toString(), out);
404         }
405     }
406 
407 
408     /***
409      * Exec the statement.
410      */
411     protected void execGroovy(String txt, PrintStream out) {
412         log("execGroovy()", Project.MSG_VERBOSE);
413 
414         // Check and ignore empty statements
415         if ("".equals(txt.trim())) {
416             return;
417         }
418 
419         log("Groovy: " + txt, Project.MSG_VERBOSE);
420 
421         //log(getClasspath().toString(),Project.MSG_VERBOSE);
422         GroovyShell groovy = null;
423         Object mavenPom = null;
424         Project project = getProject();
425         // treat the case Ant is run through Maven, and
426         if ("org.apache.commons.grant.GrantProject".equals(project.getClass().getName())) {
427             try {
428                Object propsHandler = project.getClass().getMethod("getPropsHandler", new Class[0]).invoke(project, new Object[0]);
429                Field contextField = propsHandler.getClass().getDeclaredField("context");
430                contextField.setAccessible(true);
431                Object context = contextField.get(propsHandler);
432                mavenPom = InvokerHelper.invokeMethod(context, "getProject", new Object[0]);
433             }
434             catch (Exception e) {
435                 throw new BuildException("Impossible to retrieve Maven's Ant project: " + e.getMessage(), getLocation());
436             }
437             // let ASM lookup "root" classloader
438             Thread.currentThread().setContextClassLoader(GroovyShell.class.getClassLoader());
439             // load groovy into "root.maven" classloader instead of "root" so that
440             // groovy script can access Maven classes
441             groovy = new GroovyShell(mavenPom.getClass().getClassLoader(), new Binding(), configuration);
442         } else {
443             groovy = new GroovyShell(GroovyShell.class.getClassLoader(), new Binding(), configuration);
444         }
445         try {
446             Script script = groovy.parse(txt);
447             script.setProperty("ant", new AntBuilder(project));
448             script.setProperty("project", project);
449             script.setProperty("properties", new AntProjectPropertiesDelegate(project));
450             script.setProperty("target", getOwningTarget());
451             script.setProperty("task", this);
452             if(mavenPom != null) {
453                 script.setProperty("pom", mavenPom);
454             }
455             script.run();
456         } catch (CompilationFailedException e) {
457             StringWriter writer = new StringWriter();
458             new ErrorReporter( e, false ).write( new PrintWriter(writer) );
459             String message = writer.toString();
460             throw new BuildException("Script Failed: "+ message, getLocation());
461         }
462     }
463 
464     /***
465      * print any results in the statement.
466      */
467     protected void printResults(PrintStream out) {
468         log("printResults()", Project.MSG_VERBOSE);
469         StringBuffer line = new StringBuffer();
470         out.println(line);
471         line = new StringBuffer();
472         out.println();
473     }
474 }