View Javadoc

    1   /*
    2    $Id: Groovy.java 4077 2006-09-26 19:51:42Z glaforge $
    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.Binding;
    50  import groovy.lang.GroovyClassLoader;
    51  import groovy.lang.GroovyShell;
    52  import groovy.lang.Script;
    53  import groovy.util.AntBuilder;
    54
    55  import java.io.BufferedOutputStream;
    56  import java.io.BufferedReader;
    57  import java.io.File;
    58  import java.io.FileOutputStream;
    59  import java.io.FileReader;
    60  import java.io.IOException;
    61  import java.io.PrintStream;
    62  import java.io.PrintWriter;
    63  import java.io.Reader;
    64  import java.io.StringWriter;
    65  import java.lang.reflect.Field;
    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  public class Groovy extends Task {
    88      /***
    89       * files to load
    90       */
    91      private Vector filesets = new Vector();
    92
    93      /***
    94       * input file
    95       */
    96      private File srcFile = null;
    97
    98      /***
    99       * input command
    100      */
    101     private String command = "";
    102
    103     /***
    104      * Results Output file.
    105      */
    106     private File output = null;
    107
    108     /***
    109      * Append to an existing file or overwrite it?
    110      */
    111     private boolean append = false;
    112
    113     private Path classpath;
    114
    115     /***
    116      * Compiler configuration.
    117      *
    118      * Used to specify the debug output to print stacktraces in case something fails.
    119      * TODO: Could probably be reused to specify the encoding of the files to load or other properties.
    120      */
    121     private CompilerConfiguration configuration = new CompilerConfiguration();
    122
    123     /***
    124      * Enable compiler to report stack trace information if a problem occurs
    125      * during compilation.
    126      * @param stacktrace
    127      */
    128     public void setStacktrace(boolean stacktrace) {
    129         configuration.setDebug(stacktrace);
    130     }
    131
    132
    133     /***
    134      * Set the name of the file to be run. The folder of the file is automatically added to the classpath.
    135      * Required unless statements are enclosed in the build file
    136      */
    137     public void setSrc(final File srcFile) {
    138         this.srcFile = srcFile;
    139     }
    140
    141     /***
    142      * Set an inline command to execute.
    143      * NB: Properties are not expanded in this text.
    144      */
    145     public void addText(String txt) {
    146         log("addText('"+txt+"')", Project.MSG_VERBOSE);
    147         this.command += txt;
    148     }
    149
    150     /***
    151      * Adds a set of files (nested fileset attribute).
    152      */
    153     public void addFileset(FileSet set) {
    154         filesets.addElement(set);
    155     }
    156
    157     /***
    158      * Set the output file;
    159      * optional, defaults to the Ant log.
    160      */
    161     public void setOutput(File output) {
    162         this.output = output;
    163     }
    164
    165     /***
    166      * whether output should be appended to or overwrite
    167      * an existing file.  Defaults to false.
    168      *
    169      * @since Ant 1.5
    170      */
    171     public void setAppend(boolean append) {
    172         this.append = append;
    173     }
    174
    175
    176     /***
    177      * Sets the classpath for loading.
    178      * @param classpath The classpath to set
    179      */
    180     public void setClasspath(final Path classpath) {
    181         this.classpath = classpath;
    182     }
    183
    184     /***
    185      * Returns a new path element that can be configured.
    186      * Gets called for instance by Ant when it encounters a nested <classpath> element.
    187      */
    188     public Path createClasspath() {
    189         if (this.classpath == null) {
    190             this.classpath = new Path(getProject());
    191         }
    192         return this.classpath.createPath();
    193     }
    194
    195     /***
    196      * Set the classpath for loading
    197      * using the classpath reference.
    198      */
    199     public void setClasspathRef(final Reference r) {
    200         createClasspath().setRefid(r);
    201     }
    202
    203     /***
    204      * Gets the classpath.
    205      * @return Returns a Path
    206      */
    207     public Path getClasspath() {
    208         return classpath;
    209     }
    210
    211     /***
    212      * Load the file and then execute it
    213      */
    214     public void execute() throws BuildException {
    215         log("execute()", Project.MSG_VERBOSE);
    216
    217         command = command.trim();
    218
    219         if (srcFile == null && command.length() == 0
    220             && filesets.isEmpty()) {
    221             throw new BuildException("Source file does not exist!", getLocation());
    222         }
    223
    224         if (srcFile != null && !srcFile.exists()) {
    225             throw new BuildException("Source file does not exist!", getLocation());
    226         }
    227
    228         // deal with the filesets
    229         for (int i = 0; i < filesets.size(); i++) {
    230             FileSet fs = (FileSet) filesets.elementAt(i);
    231             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
    232             File srcDir = fs.getDir(getProject());
    233
    234             String[] srcFiles = ds.getIncludedFiles();
    235         }
    236
    237         try {
    238             PrintStream out = System.out;
    239             try {
    240                 if (output != null) {
    241                     log("Opening PrintStream to output file " + output,
    242                         Project.MSG_VERBOSE);
    243                     out = new PrintStream(
    244                               new BufferedOutputStream(
    245                                   new FileOutputStream(output
    246                                                        .getAbsolutePath(),
    247                                                        append)));
    248                 }
    249
    250                 // if there are no groovy statements between the enclosing Groovy tags
    251                 // then read groovy statements in from a text file using the src attribute
    252                 if (command == null || command.trim().length() == 0) {
    253                 	createClasspath().add(new Path(getProject(), srcFile.getParentFile().getCanonicalPath()));
    254                     command = getText(new BufferedReader(new FileReader(srcFile)));
    255                 }
    256
    257
    258                 if (command != null) {
    259                     execGroovy(command,out);
    260                 } else {
    261                     throw new BuildException("Source file does not exist!", getLocation());
    262                 }
    263
    264             } finally {
    265                 if (out != null && out != System.out) {
    266                     out.close();
    267                 }
    268             }
    269         } catch (IOException e) {
    270             throw new BuildException(e, getLocation());
    271         }
    272
    273         log("statements executed successfully", Project.MSG_VERBOSE);
    274     }
    275
    276
    277     private static String getText(BufferedReader reader) throws IOException {
    278         StringBuffer answer = new StringBuffer();
    279         // reading the content of the file within a char buffer allow to keep the correct line endings
    280         char[] charBuffer = new char[4096];
    281         int nbCharRead = 0;
    282         while ((nbCharRead = reader.read(charBuffer)) != -1) {
    283             // appends buffer
    284             answer.append(charBuffer, 0, nbCharRead);
    285         }
    286         reader.close();
    287         return answer.toString();
    288     }
    289
    290
    291     /***
    292      * read in lines and execute them
    293      */
    294     protected void runStatements(Reader reader, PrintStream out)
    295         throws IOException {
    296         log("runStatements()", Project.MSG_VERBOSE);
    297
    298         StringBuffer txt = new StringBuffer();
    299         String line = "";
    300
    301         BufferedReader in = new BufferedReader(reader);
    302
    303         while ((line = in.readLine()) != null) {
    304             line = getProject().replaceProperties(line);
    305
    306             if (line.indexOf("--") >= 0) {
    307                 txt.append("\n");
    308             }
    309         }
    310         // Catch any statements not followed by ;
    311         if (!txt.equals("")) {
    312             execGroovy(txt.toString(), out);
    313         }
    314     }
    315
    316
    317     /***
    318      * Exec the statement.
    319      */
    320     protected void execGroovy(final String txt, final PrintStream out) {
    321         log("execGroovy()", Project.MSG_VERBOSE);
    322
    323         // Check and ignore empty statements
    324         if ("".equals(txt.trim())) {
    325             return;
    326         }
    327
    328         log("Groovy: " + txt, Project.MSG_VERBOSE);
    329
    330         //log(getClasspath().toString(),Project.MSG_VERBOSE);
    331         Object mavenPom = null;
    332         final Project project = getProject();
    333         final ClassLoader baseClassLoader;
    334         // treat the case Ant is run through Maven, and
    335         if ("org.apache.commons.grant.GrantProject".equals(project.getClass().getName())) {
    336             try {
    337                final Object propsHandler = project.getClass().getMethod("getPropsHandler", new Class[0]).invoke(project, new Object[0]);
    338                final Field contextField = propsHandler.getClass().getDeclaredField("context");
    339                contextField.setAccessible(true);
    340                final Object context = contextField.get(propsHandler);
    341                mavenPom = InvokerHelper.invokeMethod(context, "getProject", new Object[0]);
    342             }
    343             catch (Exception e) {
    344                 throw new BuildException("Impossible to retrieve Maven's Ant project: " + e.getMessage(), getLocation());
    345             }
    346             // let ASM lookup "root" classloader
    347             Thread.currentThread().setContextClassLoader(GroovyShell.class.getClassLoader());
    348             // load groovy into "root.maven" classloader instead of "root" so that
    349             // groovy script can access Maven classes
    350             baseClassLoader = mavenPom.getClass().getClassLoader();
    351         } else {
    352             baseClassLoader = GroovyShell.class.getClassLoader();
    353         }
    354
    355         final GroovyClassLoader classLoader = new GroovyClassLoader(baseClassLoader);
    356         addClassPathes(classLoader);
    357
    358         final GroovyShell groovy = new GroovyShell(classLoader, new Binding(), configuration);
    359         try {
    360             final Script script = groovy.parse(txt);
    361             script.setProperty("ant", new AntBuilder(project, getOwningTarget()));
    362             script.setProperty("project", project);
    363             script.setProperty("properties", new AntProjectPropertiesDelegate(project));
    364             script.setProperty("target", getOwningTarget());
    365             script.setProperty("task", this);
    366             if (mavenPom != null) {
    367                 script.setProperty("pom", mavenPom);
    368             }
    369             script.run();
    370         } catch (CompilationFailedException e) {
    371             StringWriter writer = new StringWriter();
    372             new ErrorReporter( e, false ).write( new PrintWriter(writer) );
    373             String message = writer.toString();
    374             throw new BuildException("Script Failed: "+ message, getLocation());
    375         }
    376     }
    377
    378
    379 	/***
    380 	 * Adds the class pathes (if any)
    381 	 * @param classLoader the classloader to configure
    382 	 */
    383 	protected void addClassPathes(final GroovyClassLoader classLoader)
    384 	{
    385 		if (classpath != null)
    386 		{
    387 			for (int i = 0; i < classpath.list().length; i++)
    388 			{
    389 	            classLoader.addClasspath(classpath.list()[i]);
    390 			}
    391 		}
    392 	}
    393
    394     /***
    395      * print any results in the statement.
    396      */
    397     protected void printResults(PrintStream out) {
    398         log("printResults()", Project.MSG_VERBOSE);
    399         StringBuffer line = new StringBuffer();
    400         out.println(line);
    401         line = new StringBuffer();
    402         out.println();
    403     }
    404 }