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 }