001    /*
002     $Id: Groovy.java,v 1.6 2005/07/18 22:11:48 glaforge Exp $
003    
004     Copyright 2005 (C) Jeremy Rayner. All Rights Reserved.
005    
006     Redistribution and use of this software and associated documentation
007     ("Software"), with or without modification, are permitted provided
008     that the following conditions are met:
009    
010     1. Redistributions of source code must retain copyright
011        statements and notices.  Redistributions must also contain a
012        copy of this document.
013    
014     2. Redistributions in binary form must reproduce the
015        above copyright notice, this list of conditions and the
016        following disclaimer in the documentation and/or other
017        materials provided with the distribution.
018    
019     3. The name "groovy" must not be used to endorse or promote
020        products derived from this Software without prior written
021        permission of The Codehaus.  For written permission,
022        please contact info@codehaus.org.
023    
024     4. Products derived from this Software may not be called "groovy"
025        nor may "groovy" appear in their names without prior written
026        permission of The Codehaus. "groovy" is a registered
027        trademark of The Codehaus.
028    
029     5. Due credit should be given to The Codehaus -
030        http://groovy.codehaus.org/
031    
032     THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033     ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034     NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
036     THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041     STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043     OF THE POSSIBILITY OF SUCH DAMAGE.
044    
045     */
046    
047    package org.codehaus.groovy.ant;
048    
049    import groovy.lang.GroovyShell;
050    import groovy.lang.Script;
051    import groovy.util.AntBuilder;
052    
053    import java.io.BufferedOutputStream;
054    import java.io.BufferedReader;
055    import java.io.File;
056    import java.io.FileOutputStream;
057    import java.io.FileReader;
058    import java.io.IOException;
059    import java.io.PrintStream;
060    import java.io.Reader;
061    import java.lang.reflect.Field;
062    import java.util.Hashtable;
063    import java.util.Vector;
064    
065    import org.apache.tools.ant.BuildException;
066    import org.apache.tools.ant.DirectoryScanner;
067    import org.apache.tools.ant.Project;
068    import org.apache.tools.ant.Task;
069    import org.apache.tools.ant.types.FileSet;
070    import org.apache.tools.ant.types.Path;
071    import org.apache.tools.ant.types.Reference;
072    import org.codehaus.groovy.control.CompilationFailedException;
073    import org.codehaus.groovy.runtime.InvokerHelper;
074    
075    /**
076     * Executes a series of Groovy statements.
077     *
078     * <p>Statements can
079     * either be read in from a text file using the <i>src</i> attribute or from
080     * between the enclosing groovy tags.</p>
081     *
082     *
083     * Based heavily on SQLExec.java which is part of apache-ant
084     * http://cvs.apache.org/viewcvs.cgi/ant/src/main/org/apache/tools/ant/taskdefs/SQLExec.java?rev=MAIN
085     *
086     * Copyright  2000-2005 The Apache Software Foundation
087     *
088     *  Licensed under the Apache License, Version 2.0 (the "License");
089     *  you may not use this file except in compliance with the License.
090     *  You may obtain a copy of the License at
091     *
092     *      http://www.apache.org/licenses/LICENSE-2.0
093     *
094     *  Unless required by applicable law or agreed to in writing, software
095     *  distributed under the License is distributed on an "AS IS" BASIS,
096     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
097     *  See the License for the specific language governing permissions and
098     *  limitations under the License.
099     *
100     *
101     */
102    public class Groovy extends Task {
103        /**
104         * files to load
105         */
106        private Vector filesets = new Vector();
107    
108        /**
109         * input file
110         */
111        private File srcFile = null;
112    
113        /**
114         * input command
115         */
116        private String command = "";
117    
118        /**
119         * Print results.
120         */
121        private boolean print = false;
122    
123        /**
124         * Results Output file.
125         */
126        private File output = null;
127    
128        /**
129         * Append to an existing file or overwrite it?
130         */
131        private boolean append = false;
132    
133        /**
134         * Used for caching loaders / driver. This is to avoid
135         * getting an OutOfMemoryError when calling this task
136         * multiple times in a row.
137         */
138        private static Hashtable loaderMap = new Hashtable(3);
139    
140        private Path classpath;
141    
142        /**
143         * User name.
144         */
145        private String userId = null;
146    
147        /**
148         * Groovy Version needed for this collection of statements.
149         **/
150        private String version = null;
151    
152    
153        /**
154         * Set the name of the file to be run.
155         * Required unless statements are enclosed in the build file
156         */
157        public void setSrc(File srcFile) {
158            this.srcFile = srcFile;
159        }
160    
161        /**
162         * Set an inline command to execute.
163         * NB: Properties are not expanded in this text.
164         */
165        public void addText(String txt) {
166            log("addText('"+txt+"')", Project.MSG_VERBOSE);
167            this.command += txt;
168        }
169    
170        /**
171         * Adds a set of files (nested fileset attribute).
172         */
173        public void addFileset(FileSet set) {
174            filesets.addElement(set);
175        }
176    
177        /**
178         * Print results from the statements;
179         * optional, default false
180         */
181        public void setPrint(boolean print) {
182            this.print = print;
183        }
184    
185        /**
186         * Set the output file;
187         * optional, defaults to the Ant log.
188         */
189        public void setOutput(File output) {
190            this.output = output;
191        }
192    
193        /**
194         * whether output should be appended to or overwrite
195         * an existing file.  Defaults to false.
196         *
197         * @since Ant 1.5
198         */
199        public void setAppend(boolean append) {
200            this.append = append;
201        }
202    
203    
204        /**
205         * Sets the classpath for loading.
206         * @param classpath The classpath to set
207         */
208        public void setClasspath(Path classpath) {
209            this.classpath = classpath;
210        }
211    
212        /**
213         * Add a path to the classpath for loading.
214         */
215        public Path createClasspath() {
216            if (this.classpath == null) {
217                this.classpath = new Path(getProject());
218            }
219            return this.classpath.createPath();
220        }
221    
222        /**
223         * Set the classpath for loading
224         * using the classpath reference.
225         */
226        public void setClasspathRef(Reference r) {
227            createClasspath().setRefid(r);
228        }
229    
230        /**
231         * Sets the version string, execute task only if
232         * groovy version match; optional.
233         * @param version The version to set
234         */
235        public void setVersion(String version) {
236            this.version = version;
237        }
238    
239    
240        protected static Hashtable getLoaderMap() {
241            return loaderMap;
242        }
243    
244    
245    
246    
247        /**
248         * Gets the classpath.
249         * @return Returns a Path
250         */
251        public Path getClasspath() {
252            return classpath;
253        }
254    
255        /**
256         * Gets the userId.
257         * @return Returns a String
258         */
259        public String getUserId() {
260            return userId;
261        }
262    
263        /**
264         * Set the user name for the connection; required.
265         * @param userId The userId to set
266         */
267        public void setUserid(String userId) {
268            this.userId = userId;
269        }
270    
271        /**
272         * Gets the version.
273         * @return Returns a String
274         */
275        public String getVersion() {
276            return version;
277        }
278    
279        /**
280         * Load the file and then execute it
281         */
282        public void execute() throws BuildException {
283            log("execute()", Project.MSG_VERBOSE);
284    
285            command = command.trim();
286    
287            try {
288                if (srcFile == null && command.length() == 0
289                    && filesets.isEmpty()) {
290                    throw new BuildException("Source file does not exist!", getLocation());
291                }
292    
293                if (srcFile != null && !srcFile.exists()) {
294                    throw new BuildException("Source file does not exist!", getLocation());
295                }
296    
297                // deal with the filesets
298                for (int i = 0; i < filesets.size(); i++) {
299                    FileSet fs = (FileSet) filesets.elementAt(i);
300                    DirectoryScanner ds = fs.getDirectoryScanner(getProject());
301                    File srcDir = fs.getDir(getProject());
302    
303                    String[] srcFiles = ds.getIncludedFiles();
304                }
305    
306                try {
307                    PrintStream out = System.out;
308                    try {
309                        if (output != null) {
310                            log("Opening PrintStream to output file " + output,
311                                Project.MSG_VERBOSE);
312                            out = new PrintStream(
313                                      new BufferedOutputStream(
314                                          new FileOutputStream(output
315                                                               .getAbsolutePath(),
316                                                               append)));
317                        }
318    
319                        // if there are no groovy statements between the enclosing Groovy tags
320                        // then read groovy statements in from a text file using the src attribute
321                        if (command == null || command.trim().length() == 0) {
322                            command = getText(new BufferedReader(new FileReader(srcFile)));
323                        }
324    
325                        
326                        if (command != null) {
327                            execGroovy(command,out);
328                        } else {
329                            throw new BuildException("Source file does not exist!", getLocation());
330                        }
331    
332                    } finally {
333                        if (out != null && out != System.out) {
334                            out.close();
335                        }
336                    }
337                } catch (IOException e) {
338                    throw new BuildException(e, getLocation());
339                }
340    
341                log("statements executed successfully");
342            } finally{}
343        }
344    
345    
346        private static String getText(BufferedReader reader) throws IOException {
347            StringBuffer answer = new StringBuffer();
348            // reading the content of the file within a char buffer allow to keep the correct line endings
349            char[] charBuffer = new char[4096];
350            int nbCharRead = 0;
351            while ((nbCharRead = reader.read(charBuffer)) != -1) {
352                // appends buffer
353                answer.append(charBuffer, 0, nbCharRead);
354            }
355            reader.close();
356            return answer.toString();
357        }
358    
359    
360        /**
361         * read in lines and execute them
362         */
363        protected void runStatements(Reader reader, PrintStream out)
364            throws IOException {
365            log("runStatements()", Project.MSG_VERBOSE);
366    
367            StringBuffer txt = new StringBuffer();
368            String line = "";
369    
370            BufferedReader in = new BufferedReader(reader);
371    
372            while ((line = in.readLine()) != null) {
373                line = getProject().replaceProperties(line);
374    
375                if (line.indexOf("--") >= 0) {
376                    txt.append("\n");
377                }
378            }
379            // Catch any statements not followed by ;
380            if (!txt.equals("")) {
381                execGroovy(txt.toString(), out);
382            }
383        }
384    
385    
386        /**
387         * Exec the statement.
388         */
389        protected void execGroovy(String txt, PrintStream out) {
390            log("execGroovy()", Project.MSG_VERBOSE);
391    
392            // Check and ignore empty statements
393            if ("".equals(txt.trim())) {
394                return;
395            }
396    
397                log("Groovy: " + txt, Project.MSG_VERBOSE);
398    
399                //log(getClasspath().toString(),Project.MSG_VERBOSE);
400                GroovyShell groovy = new GroovyShell(GroovyShell.class.getClassLoader());
401    
402                try {
403                    Script script = groovy.parse(txt);
404                    Project project = getProject();
405                    script.setProperty("ant", new AntBuilder(project));
406                    script.setProperty("project", project);
407                    script.setProperty("properties", new AntProjectPropertiesDelegate(project));
408                    script.setProperty("target", getOwningTarget());
409                    script.setProperty("task", this);
410    
411                    // treat the case Ant is run through Maven, and
412                    if ("org.apache.commons.grant.GrantProject".equals(project.getClass().getName())) {
413                        try {
414                            Object propsHandler = project.getClass().getMethod("getPropsHandler", new Class[0]).invoke(project, new Object[0]);
415                            Field contextField = propsHandler.getClass().getDeclaredField("context");
416                            contextField.setAccessible(true);
417                            Object context = contextField.get(propsHandler);
418                            Object mavenPom = InvokerHelper.invokeMethod(context, "getProject", new Object[0]);
419                            script.setProperty("pom", mavenPom);
420                        } catch (Exception e) {
421                            throw new BuildException("Impossible to retrieve Maven's Ant project: " + e.getMessage(), getLocation());
422                        }
423                    }
424    
425                    script.run();
426                } catch (CompilationFailedException e) {
427                    throw new BuildException("Script Failed: "+ e.getMessage(), getLocation());
428                }
429    
430                if (print) {
431                    StringBuffer line = new StringBuffer();
432                    line.append( " foo bar");
433                    out.println(line);
434                }
435    
436    
437        }
438    
439        /**
440         * print any results in the statement.
441         */
442        protected void printResults(PrintStream out) {
443                log("printResults()", Project.MSG_VERBOSE);
444                StringBuffer line = new StringBuffer();
445                out.println(line);
446                line = new StringBuffer();
447            out.println();
448        }
449    }