/* * Copyright 2003-2011 the original author or authors. * Copyright 2013 James Moger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.moxie.ant; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.text.MessageFormat; import java.util.Vector; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Java; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.Path; import org.moxie.Build; import org.moxie.Dependency; import org.moxie.Scope; import org.moxie.Toolkit.Key; import org.moxie.console.Console; import org.moxie.utils.FileUtils; import org.moxie.utils.StringUtils; /** * Executes a series of Groovy statements. * <p/> * <p>Statements can either be read in from a text file using * the <i>src</i> attribute or from between the enclosing groovy tags.</p> * * This class and it's companion, GroovyEngine, were originally harvested from * Groovy 1.8.8. They have been split into two to allow dynamically loading * Groovy from the Moxie cache. If they were one class, as originally written, * Ant would throw a ClassDefNotFoundException on the task unless Groovy is * part of the Ant runtime classpath at task classloading. * */ public class MxGroovy extends Java { Boolean showtitle; Scope scope; Vector<FileSet> filesets = new Vector<FileSet>(); File srcFile = null; String command = ""; String subtitle = "embedded script"; File output = null; boolean append = false; boolean fork = false; boolean includeAntRuntime = true; boolean useGroovyShell = false; boolean contextClassLoader; boolean stacktrace; public MxGroovy() { super(); setTaskName("mx:groovy"); } public void setShowtitle(boolean value) { this.showtitle = value; } public boolean isShowTitle() { return showtitle == null || showtitle; } public void setScope(String value) { this.scope = Scope.fromString(value); } public void setSubtitle(String value) { this.subtitle = value; } /** * Should the script be executed using a forked process. Defaults to false. * * @param fork true if the script should be executed in a forked process */ public void setFork(boolean fork) { super.setFork(fork); this.fork = fork; } /** * Should a new GroovyShell be used when forking. Special variables won't be available * but you don't need Ant in the classpath. * * @param useGroovyShell true if GroovyShell should be used to run the script directly */ public void setUseGroovyShell(boolean useGroovyShell) { this.useGroovyShell = useGroovyShell; } /** * Should the system classpath be included on the classpath when forking. Defaults to true. * * @param includeAntRuntime true if the system classpath should be on the classpath */ public void setIncludeAntRuntime(boolean includeAntRuntime) { this.includeAntRuntime = includeAntRuntime; } /** * Enable compiler to report stack trace information if a problem occurs * during compilation. * * @param stacktrace set to true to enable stacktrace reporting */ public void setStacktrace(boolean stacktrace) { this.stacktrace = stacktrace; } /** * Set the name of the file to be run. The folder of the file is automatically added to the classpath. * Required unless statements are enclosed in the build file * * @param srcFile the file containing the groovy script to execute */ public void setSrc(File srcFile) { this.srcFile = srcFile; } /** * Set an inline command to execute. * NB: Properties are not expanded in this text. * * @param txt the inline groovy ommands to execute */ public void addText(String txt) { log("addText('" + txt + "')", Project.MSG_VERBOSE); this.command += txt; } /** * Adds a set of files (nested fileset attribute). * * @param set the fileset representing source files */ public void addFileset(FileSet set) { filesets.addElement(set); } /** * Set the output file; * optional, defaults to the Ant log. * * @param output the output file */ public void setOutput(File output) { this.output = output; } /** * Whether output should be appended to or overwrite * an existing file. Defaults to false. * * @param append set to true to append */ public void setAppend(boolean append) { this.append = append; } /** * Setting to true will cause the contextClassLoader to be set with * the classLoader of the shell used to run the script. Not used if * fork is true. Not allowed when running from Maven but in that * case the context classLoader is set appropriately for Maven. * * @param contextClassLoader set to true to set the context classloader */ public void setContextClassLoader(boolean contextClassLoader) { this.contextClassLoader = contextClassLoader; } Build getBuild() { return (Build) getProject().getReference(Key.build.referenceId()); } Console getConsole() { return getBuild().getConsole(); } /** * Load the file and then execute it */ public void execute() throws BuildException { Build build = (Build) getProject().getReference(Key.build.referenceId()); MxTask.loadRuntimeDependencies(build, new Dependency("mx:groovy")); if (scope != null) { createProjectClasspath(build); } if (fork) { createForkClasspath(); } if (srcFile == null && StringUtils.isEmpty(command) && filesets.isEmpty()) { throw new BuildException("Source file does not exist!", getLocation()); } if (srcFile != null && !srcFile.exists()) { throw new BuildException(MessageFormat.format("Source file {0} does not exist!", srcFile), getLocation()); } // if there are no groovy statements between the enclosing Groovy tags // then read groovy statements in from a text file using the src attribute if (StringUtils.isEmpty(command)) { createClasspath().add(new Path(getProject(), srcFile.getParentFile().getAbsolutePath())); command = FileUtils.readContent(srcFile, "\n"); } if (StringUtils.isEmpty(command)) { throw new BuildException("No Groovy statements to execute!", getLocation()); } if (isShowTitle()) { build.getConsole().title(getClass(), srcFile != null ? srcFile.getAbsolutePath() : subtitle); } PrintStream out = System.out; try { GroovyEngine groovy = new GroovyEngine(); if (fork) { groovy.forkGroovy(this, command); } else { if (output != null) { build.getConsole().debug("Opening PrintStream to output file " + output); out = new PrintStream( new BufferedOutputStream( new FileOutputStream(output.getAbsolutePath(), append))); } groovy.execGroovy(this, command, out); } } catch (IOException e) { throw new BuildException(e, getLocation()); } finally { if (out != null && out != System.out) { out.close(); } } } void createProjectClasspath(Build build) { Path classpath = createClasspath(); // add project compiled output path classpath.createPathElement().setLocation(build.getConfig().getOutputDirectory(scope)); if (!scope.isDefault()) { classpath.createPathElement().setLocation(build.getConfig().getOutputDirectory(Scope.compile)); } // add resource directories for (File dir : build.getConfig().getResourceDirectories(scope)) { classpath.createPathElement().setLocation(dir); } if (!scope.isDefault()) { for (File dir : build.getConfig().getResourceDirectories(Scope.compile)) { classpath.createPathElement().setLocation(dir); } } // add jar classpaths for (File jarFile : build.getSolver().getClasspath(scope)) { classpath.createPathElement().setLocation(jarFile); } // add linked projects compiled output path, resource directories, and jar files for (Build linkedProject : build.getSolver().getLinkedModules()) { classpath.createPathElement().setLocation(linkedProject.getConfig().getOutputDirectory(Scope.compile)); for (File dir : linkedProject.getConfig().getResourceDirectories(Scope.compile)) { classpath.createPathElement().setLocation(dir); } for (File jarFile : linkedProject.getSolver().getClasspath(Scope.compile)) { classpath.createPathElement().setLocation(jarFile); } } } void createForkClasspath() { Path path; if (includeAntRuntime) { path = createClasspath(); path.setPath(System.getProperty("java.class.path")); } String groovyHome = null; final String[] strings = getSysProperties().getVariables(); if (strings != null) { for (String prop : strings) { if (prop.startsWith("-Dgroovy.home=")) { groovyHome = prop.substring("-Dgroovy.home=".length()); } } } if (groovyHome == null) { groovyHome = System.getProperty("groovy.home"); } if (groovyHome == null) { groovyHome = System.getenv("GROOVY_HOME"); } if (groovyHome == null) { throw new IllegalStateException("Neither ${groovy.home} nor GROOVY_HOME defined."); } File jarDir = new File(groovyHome, "embeddable"); if (!jarDir.exists()) { throw new IllegalStateException("GROOVY_HOME incorrectly defined. No embeddable directory found in: " + groovyHome); } final File[] files = jarDir.listFiles(); if (files != null) { for (File file : files) { try { getConsole().debug("Adding jar to classpath: " + file.getCanonicalPath()); } catch (IOException e) { // ignore } path = createClasspath(); path.setLocation(file); } } } void executeFork() { getConsole().debug(getCommandLine().describeCommand()); super.execute(); } }