From 4bd9b4a6e473a9188124989315f75b778ad2a363 Mon Sep 17 00:00:00 2001 From: pyxide Date: Fri, 13 Jan 2017 17:16:50 +0100 Subject: [PATCH] Scriptdef task new options : 'compiled' (if javax.script engine implements Compilable), 'encoding' to load resources --- .../taskdefs/optional/script/ScriptDef.java | 22 +++- .../tools/ant/util/ScriptRunnerBase.java | 87 ++++++++++++-- .../tools/ant/util/ScriptRunnerHelper.java | 40 +++++++ .../ant/util/optional/JavaxScriptRunner.java | 108 +++++++++++++++--- 4 files changed, 230 insertions(+), 27 deletions(-) diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java b/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java index ac9eb88ec..3f40a14b6 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java @@ -67,7 +67,7 @@ public class ScriptDef extends DefBase { /** * Set the project. - * @param project the project that this def belows to. + * @param project the project that this definition belongs to. */ public void setProject(Project project) { super.setProject(project); @@ -237,7 +237,6 @@ public class ScriptDef extends DefBase { + "attributes"); } - nestedElementMap.put(nestedElement.name, nestedElement); } @@ -367,6 +366,15 @@ public class ScriptDef extends DefBase { helper.setLanguage(language); } + /** + * Defines the language (required). + * + * @param language the scripting language name for the script. + */ + public void setCompiled(boolean compiled) { + helper.setCompiled(compiled); + } + /** * Load the script from an external file ; optional. * @@ -376,6 +384,15 @@ public class ScriptDef extends DefBase { helper.setSrc(file); } + /** + * Set the encoding of the script from an external file ; optional. + * + * @param encoding the encoding of the file containing the script source. + */ + public void setEncoding(String encoding) { + helper.setEncoding(encoding); + } + /** * Set the script text. * @@ -394,4 +411,3 @@ public class ScriptDef extends DefBase { helper.add(resource); } } - diff --git a/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java b/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java index b8aa01a81..ac08f98bc 100644 --- a/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java +++ b/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java @@ -19,12 +19,13 @@ package org.apache.tools.ant.util; import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -34,6 +35,8 @@ import org.apache.tools.ant.Project; import org.apache.tools.ant.ProjectComponent; import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.types.ResourceCollection; +import org.apache.tools.ant.types.resources.PropertyResource; +import org.apache.tools.ant.types.resources.StringResource; /** * This is a common abstract base case for script runners. @@ -51,6 +54,11 @@ public abstract class ScriptRunnerBase { /** Script content */ private String script = ""; + private String encoding; + + /** Enable script compilation. */ + private boolean compiled; + /** Project this runner is used in */ private Project project; @@ -186,8 +194,36 @@ public abstract class ScriptRunnerBase { return keepEngine; } + /** + * Whether to use script compilation if available. + * @since Ant 1.10.1 + * @param compiled if true, compile the script if possible. + */ + public final void setCompiled(boolean compiled) { + this.compiled = compiled; + } + + /** + * Get the compiled attribute. + * @since Ant 1.10.1 + * @return the attribute. + */ + public final boolean getCompiled() { + return compiled; + } + + /** + * Set encoding of the script from an external file; optional. + * @since Ant 1.10.1 + * @param encoding encoding of the external file containing the script source. + */ + public void setEncoding(String encoding) { + this.encoding = encoding; + } + /** * Load the script from an external file; optional. + * @since Ant 1.10.1 * @param file the file containing the script source. */ public void setSrc(File file) { @@ -195,12 +231,19 @@ public abstract class ScriptRunnerBase { if (!file.exists()) { throw new BuildException("file " + filename + " not found."); } + InputStream in = null; try { - readSource(new FileReader(file), filename); + in = new FileInputStream(file); } catch (FileNotFoundException e) { //this can only happen if the file got deleted a short moment ago throw new BuildException("file " + filename + " not found."); } + + try { + readSource(in, filename); + } finally { + FileUtils.close(in); + } } /** @@ -208,19 +251,25 @@ public abstract class ScriptRunnerBase { * @param reader the reader; this is closed afterwards. * @param name the name to use in error messages */ - private void readSource(Reader reader, String name) { - BufferedReader in = null; + private void readSource(InputStream in, String name) { + Reader reader = null; try { - in = new BufferedReader(reader); - script += FileUtils.safeReadFully(in); + if (null == encoding) { + reader = new InputStreamReader(in); + } else { + reader = new InputStreamReader(in, encoding); + } + reader = new BufferedReader(reader); + script += FileUtils.safeReadFully(reader); + } catch(UnsupportedEncodingException e) { + throw new BuildException("Failed to decode " + name + " with encoding " + encoding, e); } catch (IOException ex) { throw new BuildException("Failed to read " + name, ex); } finally { - FileUtils.close(in); + FileUtils.close(reader); } } - /** * Add a resource to the source list. * @since Ant 1.7.1 @@ -228,6 +277,20 @@ public abstract class ScriptRunnerBase { * @throws BuildException if the resource cannot be read */ public void loadResource(Resource sourceResource) { + if(sourceResource instanceof StringResource) { + // Note: StringResource uses UTF-8 by default to encode/decode, not the default platform encoding + script += ((StringResource) sourceResource).getValue(); + return; + } + if(sourceResource instanceof PropertyResource) { + script += ((PropertyResource) sourceResource).getValue(); + return; + } + + // Concat resource + + // FileResource : OK for default encoding + String name = sourceResource.toLongString(); InputStream in = null; try { @@ -238,7 +301,12 @@ public abstract class ScriptRunnerBase { throw new BuildException( "Failed to open " + name + " -it is not readable", e); } - readSource(new InputStreamReader(in), name); + + try { + readSource(in, name); + } finally { + FileUtils.close(in); + } } /** @@ -356,5 +424,4 @@ public abstract class ScriptRunnerBase { Thread.currentThread().setContextClassLoader( origLoader); } - } diff --git a/src/main/org/apache/tools/ant/util/ScriptRunnerHelper.java b/src/main/org/apache/tools/ant/util/ScriptRunnerHelper.java index 9e814d966..5cc23ab01 100644 --- a/src/main/org/apache/tools/ant/util/ScriptRunnerHelper.java +++ b/src/main/org/apache/tools/ant/util/ScriptRunnerHelper.java @@ -31,9 +31,11 @@ import org.apache.tools.ant.types.resources.Union; public class ScriptRunnerHelper { private ClasspathUtils.Delegate cpDelegate = null; private File srcFile; + private String encoding; private String manager = "auto"; private String language; private String text; + private boolean compiled = false; private boolean setBeans = true; private ProjectComponent projectComponent; private ClassLoader scriptLoader = null; @@ -53,6 +55,12 @@ public class ScriptRunnerHelper { */ public ScriptRunnerBase getScriptRunner() { ScriptRunnerBase runner = getRunner(); + runner.setCompiled(compiled); + + if (encoding != null) { + // set it first, because runner.setSrc() loads immediately the file + runner.setEncoding(encoding); + } if (srcFile != null) { runner.setSrc(srcFile); } @@ -107,6 +115,16 @@ public class ScriptRunnerHelper { this.srcFile = file; } + /** + * Set the encoding of the script from an external file ; optional. + * + * @param encoding the encoding of the file containing the script source. + * @since Ant 1.10.1 + */ + public void setEncoding(String encoding) { + this.encoding = encoding; + } + /** * Add script text. * @@ -142,6 +160,28 @@ public class ScriptRunnerHelper { return language; } + /** + * Enable the compilation of the script if possible. + * If this is true and the compilation feature is available in + * the script engine, the script is compiled before the first + * evaluation, and should be cached for future evaluations. + * Otherwise, a script is evaluated is used. + * The default is false. + * + * @param compiled the value to set. + */ + public void setCompiled(boolean compiled) { + this.compiled = compiled; + } + + /** + * Get the compilation feature. + * @return the compilation feature. + */ + public boolean getCompiled() { + return this.compiled; + } + /** * Set the setbeans attribute. * If this is true, <script> will create variables in the diff --git a/src/main/org/apache/tools/ant/util/optional/JavaxScriptRunner.java b/src/main/org/apache/tools/ant/util/optional/JavaxScriptRunner.java index 7a5cfa4d3..2670b0907 100644 --- a/src/main/org/apache/tools/ant/util/optional/JavaxScriptRunner.java +++ b/src/main/org/apache/tools/ant/util/optional/JavaxScriptRunner.java @@ -21,6 +21,7 @@ package org.apache.tools.ant.util.optional; import java.util.Iterator; import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; import org.apache.tools.ant.util.ReflectWrapper; import org.apache.tools.ant.util.ScriptRunnerBase; @@ -31,6 +32,11 @@ import org.apache.tools.ant.util.ScriptRunnerBase; public class JavaxScriptRunner extends ScriptRunnerBase { private ReflectWrapper engine; + /** Debug constant */ + private static final boolean DEBUG = Boolean.getBoolean("JavaxScriptRunner.DEBUG"); + + private String compiledScriptRefName; + /** * Get the name of the manager prefix. * @return "javax" @@ -79,28 +85,85 @@ public class JavaxScriptRunner extends ScriptRunnerBase { checkLanguage(); ClassLoader origLoader = replaceContextLoader(); try { + + if(DEBUG) System.out.println("-- JavaxScriptRunner.evaluateScript : compile enabled [" + getCompiled() + "]"); + + if (getCompiled()) { + + if (null == compiledScriptRefName) { + compiledScriptRefName = execName + ".compiledScript.0123456789"; + } + ReflectWrapper scriptRefObj = getProject().getReference(compiledScriptRefName); + + if (null == scriptRefObj) { + + ReflectWrapper engine = createEngine(); + if (engine == null) { + throw new BuildException( + "Unable to create javax script engine for " + + getLanguage()); + } + + final Class engineClass = Class.forName("javax.script.ScriptEngine"); + final Class compilableClass = Class.forName("javax.script.Compilable"); + final Object wrappedObject = engine.getObject(); + + if (DEBUG) System.out.println("-- JavaxScriptRunner.evaluateScript : wrappedObject [" + wrappedObject.getClass().getName() + "]"); + if (engineClass.isAssignableFrom(wrappedObject.getClass()) && compilableClass.isAssignableFrom(wrappedObject.getClass())) { + + if(DEBUG) System.out.println("-- JavaxScriptRunner.evaluateScript : compilable [" + wrappedObject.getClass().getName() + "]"); + + { + getProject().log("compile script" + compiledScriptRefName, Project.MSG_VERBOSE); + + // compilable engine + final Object compiledScript = engine.invoke("compile", String.class, getScript()); + scriptRefObj = new ReflectWrapper(compiledScript); + } + + getProject().log("store compiled script, ref " + compiledScriptRefName, Project.MSG_DEBUG); + + } else { + getProject().log("script compilation not available", Project.MSG_DEBUG); + scriptRefObj = new ReflectWrapper(null); + } + + getProject().addReference(compiledScriptRefName, scriptRefObj); + } + + if (null != scriptRefObj.getObject()) { + + if (DEBUG) System.out.println("-- JavaxScriptRunner.evaluateScript : execute compiled script"); + + final Object simpleBindings; + { + final Class simpleBindingsClass = Class.forName("javax.script.SimpleBindings"); + simpleBindings = simpleBindingsClass.newInstance(); + } + + applyBindings(new ReflectWrapper(simpleBindings)); + if (DEBUG) System.out.println("-- JavaxScriptRunner.evaluateScript : bindings applied"); + + getProject().log("run compiled script, ref " + compiledScriptRefName, Project.MSG_DEBUG); + + final Class bindingsClass = Class.forName("javax.script.Bindings"); + + return scriptRefObj.invoke("eval", bindingsClass, simpleBindings); + } + } + ReflectWrapper engine = createEngine(); if (engine == null) { throw new BuildException( "Unable to create javax script engine for " + getLanguage()); } - for (Iterator i = getBeans().keySet().iterator(); i.hasNext();) { - String key = (String) i.next(); - Object value = getBeans().get(key); - if ("FX".equalsIgnoreCase(getLanguage())) { - engine.invoke( - "put", String.class, key - + ":" + value.getClass().getName(), - Object.class, value); - } else { - engine.invoke( - "put", String.class, key, - Object.class, value); - } - } + + applyBindings(engine); + // execute the script return engine.invoke("eval", String.class, getScript()); + } catch (BuildException be) { //catch and rethrow build exceptions @@ -126,6 +189,23 @@ public class JavaxScriptRunner extends ScriptRunnerBase { } } + private void applyBindings(ReflectWrapper engine) { + for (Iterator i = getBeans().keySet().iterator(); i.hasNext();) { + String key = (String) i.next(); + Object value = getBeans().get(key); + if ("FX".equalsIgnoreCase(getLanguage())) { + engine.invoke( + "put", String.class, key + + ":" + value.getClass().getName(), + Object.class, value); + } else { + engine.invoke( + "put", String.class, key, + Object.class, value); + } + } + } + private ReflectWrapper createEngine() { if (engine != null) { return engine;