Author: peterreilly Date: Thu Nov 23 14:51:44 2006 New Revision: 478700 URL: http://svn.apache.org/viewvc?view=rev&rev=478700 Log: Refactoring ScriptRunner * split into general non-bsf dependant part and bsf-dependant part * get classloader to be set outside scriptrunner * allow script engine to be reused in other scripts (not used in code yet) * add api to check if language supported (not used in code yet) Mods from Bugzilla 40908 * add clearing of text (not used in code yet) * add impl of eval (not used in code yet) * use fileutils reader functionality (note: need to allow setting of file encoding)
Added: ant/core/trunk/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/Script.java ant/core/trunk/src/main/org/apache/tools/ant/util/optional/ScriptRunner.java Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/Script.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/Script.java?view=diff&rev=478700&r1=478699&r2=478700 ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/Script.java (original) +++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/Script.java Thu Nov 23 14:51:44 2006 @@ -21,6 +21,7 @@ import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.apache.tools.ant.util.optional.ScriptRunner; +import org.apache.tools.ant.util.ScriptRunnerBase; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Reference; @@ -43,7 +44,7 @@ * @exception BuildException if something goes wrong with the build */ public void execute() throws BuildException { - ScriptRunner runner = new ScriptRunner(); + ScriptRunnerBase runner = new ScriptRunner(); if (language != null) { runner.setLanguage(language); } @@ -54,7 +55,9 @@ runner.addText(text); } if (classpath != null) { - runner.setClasspath(classpath); + runner.setScriptClassLoader( + getProject().createClassLoader( + getClass().getClassLoader(), classpath)); } if (setBeans) { runner.bindToComponent(this); Added: ant/core/trunk/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java?view=auto&rev=478700 ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java (added) +++ ant/core/trunk/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java Thu Nov 23 14:51:44 2006 @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.tools.ant.util; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.Project; + +import java.util.Map; +import java.util.HashMap; +import java.util.Iterator; + +/** + * This is a common abstract base case for script runners. + * These classes need to implement executeScript, evalulateScript + * and supportsLanguage. + */ +public abstract class ScriptRunnerBase { + /** Whether to keep the engine between calls to execute/eval */ + private boolean keepEngine = false; + + /** Script language */ + private String language; + + /** Script content */ + private String script = ""; + + /** Project this runner is used in */ + private Project project; + + /** Classloader to be used when running the script. */ + private ClassLoader scriptLoader; + + /** Beans to be provided to the script */ + private Map beans = new HashMap(); + + /** + * Add a list of named objects to the list to be exported to the script + * + * @param dictionary a map of objects to be placed into the script context + * indexed by String names. + */ + public void addBeans(Map dictionary) { + for (Iterator i = dictionary.keySet().iterator(); i.hasNext();) { + String key = (String) i.next(); + try { + Object val = dictionary.get(key); + addBean(key, val); + } catch (BuildException ex) { + // The key is in the dictionary but cannot be retrieved + // This is usually due references that refer to tasks + // that have not been taskdefed in the current run. + // Ignore + } + } + } + + /** + * Add a single object into the script context. + * + * @param key the name in the context this object is to stored under. + * @param bean the object to be stored in the script context. + */ + public void addBean(String key, Object bean) { + boolean isValid = key.length() > 0 + && Character.isJavaIdentifierStart(key.charAt(0)); + + for (int i = 1; isValid && i < key.length(); i++) { + isValid = Character.isJavaIdentifierPart(key.charAt(i)); + } + + if (isValid) { + beans.put(key, bean); + } + } + + /** + * Get the beans used for the script. + * @return the map of beans. + */ + protected Map getBeans() { + return beans; + } + + /** + * Do the work. + * @param execName the name that will be passed to BSF for this script + * execution. + */ + public abstract void executeScript(String execName); + + /** + * Evalulate the script. + * @param execName the name that will be passed to BSF for this script + * execution. + * @return the result of evalulating the script. + */ + public abstract Object evalulateScript(String execName); + + /** + * Check if a script engine can be created for + * this language. + * @return true if a script engine can be created, false + * otherwise. + */ + public abstract boolean supportsLanguage(); + + /** + * Defines the language (required). + * @param language the scripting language name for the script. + */ + public void setLanguage(String language) { + this.language = language; + } + + /** + * Get the script language + * @return the script language + */ + public String getLanguage() { + return language; + } + + /** + * Set the script classloader. + * @param classLoader the classloader to use. + */ + public void setScriptClassLoader(ClassLoader classLoader) { + this.scriptLoader = classLoader; + } + + /** + * Get the classloader used to load the script engine. + * @return the classloader. + */ + protected ClassLoader getScriptClassLoader() { + return scriptLoader; + } + + /** + * Whether to keep the script engine between calls. + * @param keepEngine if true, keep the engine. + */ + public void setKeepEngine(boolean keepEngine) { + this.keepEngine = keepEngine; + } + + /** + * Get the keep engine attribute. + * @return the attribute. + */ + public boolean getKeepEngine() { + return keepEngine; + } + + /** + * Load the script from an external file; optional. + * @param file the file containing the script source. + */ + public void setSrc(File file) { + if (!file.exists()) { + throw new BuildException("file " + file.getPath() + " not found."); + } + BufferedReader in = null; + try { + in = new BufferedReader(new FileReader(file)); + script += FileUtils.readFully(in); + } catch (IOException ex) { + throw new BuildException(ex); + } finally { + FileUtils.close(in); + } + } + + /** + * Set the script text. + * + * @param text a component of the script text to be added. + */ + public void addText(String text) { + this.script += text; + } + + /** + * Get the current script text content. + * @return the script text. + */ + public String getScript() { + return script; + } + + /** + * Clear the current script text content. + */ + public void clearScript() { + this.script = ""; + } + + /** + * Bind the runner to a project component. + * Properties, targets and references are all added as beans; + * project is bound to project, and self to the component. + * @param component to become <code>self</code> + */ + public void bindToComponent(ProjectComponent component) { + project = component.getProject(); + addBeans(project.getProperties()); + addBeans(project.getUserProperties()); + addBeans(project.getTargets()); + addBeans(project.getReferences()); + addBean("project", project); + addBean("self", component); + } + + /** + * Bind the runner to a project component. + * The project and self are the only beans set. + * @param component to become <code>self</code> + */ + public void bindToComponentMinimum(ProjectComponent component) { + project = component.getProject(); + addBean("project", project); + addBean("self", component); + } + + /** + * Check if the language attribute is set. + * @throws BuildException if it is not. + */ + protected void checkLanguage() { + if (language == null) { + throw new BuildException( + "script language must be specified"); + } + } + + /** + * Replace the current context classloader with the + * script context classloader. + * @return the current context classloader. + */ + protected ClassLoader replaceContextLoader() { + ClassLoader origContextClassLoader = + Thread.currentThread().getContextClassLoader(); + if (getScriptClassLoader() == null) { + setScriptClassLoader(getClass().getClassLoader()); + } + Thread.currentThread().setContextClassLoader(getScriptClassLoader()); + return origContextClassLoader; + } + + /** + * Restore the context loader with the original context classloader. + * + * script context loader. + * @param origLoader the original context classloader. + */ + protected void restoreContextLoader(ClassLoader origLoader) { + Thread.currentThread().setContextClassLoader( + origLoader); + } + +} Modified: ant/core/trunk/src/main/org/apache/tools/ant/util/optional/ScriptRunner.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/util/optional/ScriptRunner.java?view=diff&rev=478700&r1=478699&r2=478700 ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/util/optional/ScriptRunner.java (original) +++ ant/core/trunk/src/main/org/apache/tools/ant/util/optional/ScriptRunner.java Thu Nov 23 14:51:44 2006 @@ -17,35 +17,22 @@ */ package org.apache.tools.ant.util.optional; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; - import org.apache.bsf.BSFException; import org.apache.bsf.BSFManager; +import org.apache.bsf.BSFEngine; import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.ProjectComponent; -import org.apache.tools.ant.Project; - -import org.apache.tools.ant.util.FileUtils; -import java.util.Map; -import java.util.HashMap; import java.util.Iterator; -import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.ScriptRunnerBase; /** * This class is used to run BSF scripts * */ -public class ScriptRunner { - - // Register Groovy ourselves, since BSF does not - // natively support it (yet). - // This "hack" can be removed once BSF has been - // modified to support Groovy or more dynamic - // registration. +public class ScriptRunner extends ScriptRunnerBase { + // Register Groovy ourselves, since BSF did not + // natively support it in versions previous to 1.2.4. static { BSFManager.registerScriptingEngine( "groovy", @@ -53,58 +40,30 @@ new String[] {"groovy", "gy"}); } - /** Script language */ - private String language; - - /** Script content */ - private String script = ""; - - /** Beans to be provided to the script */ - private Map beans = new HashMap(); - - /** Classpath to be used when running the script. */ - private Path classpath = null; - - /** Project this runner is used in */ - private Project project = null; + private BSFEngine engine; + private BSFManager manager; /** - * Add a list of named objects to the list to be exported to the script - * - * @param dictionary a map of objects to be placed into the script context - * indexed by String names. + * Check if bsf supports the language. + * @return true if bsf can create an engine for this language. */ - public void addBeans(Map dictionary) { - for (Iterator i = dictionary.keySet().iterator(); i.hasNext();) { - String key = (String) i.next(); - try { - Object val = dictionary.get(key); - addBean(key, val); - } catch (BuildException ex) { - // The key is in the dictionary but cannot be retrieved - // This is usually due references that refer to tasks - // that have not been taskdefed in the current run. - // Ignore - } + public boolean supportsLanguage() { + if (manager != null) { + return true; } - } - - /** - * Add a single object into the script context. - * - * @param key the name in the context this object is to stored under. - * @param bean the object to be stored in the script context. - */ - public void addBean(String key, Object bean) { - boolean isValid = key.length() > 0 - && Character.isJavaIdentifierStart(key.charAt(0)); - - for (int i = 1; isValid && i < key.length(); i++) { - isValid = Character.isJavaIdentifierPart(key.charAt(i)); - } - - if (isValid) { - beans.put(key, bean); + checkLanguage(); + ClassLoader origLoader = replaceContextLoader(); + try { + BSFManager m = createManager(); + BSFEngine e = + engine != null + ? engine + : m.loadScriptingEngine(getLanguage()); + return e != null; + } catch (Exception ex) { + return false; + } finally { + restoreContextLoader(origLoader); } } @@ -117,139 +76,99 @@ * @exception BuildException if someting goes wrong exectuing the script. */ public void executeScript(String execName) throws BuildException { - if (language == null) { - throw new BuildException("script language must be specified"); - } - - ClassLoader origContextClassLoader = - Thread.currentThread().getContextClassLoader(); - ClassLoader scriptLoader = getClass().getClassLoader(); - if (classpath != null && project != null) { - scriptLoader = project.createClassLoader( - scriptLoader, classpath); - } + checkLanguage(); + ClassLoader origLoader = replaceContextLoader(); try { - Thread.currentThread().setContextClassLoader(scriptLoader); - BSFManager manager = new BSFManager (); - manager.setClassLoader(scriptLoader); - - for (Iterator i = beans.keySet().iterator(); i.hasNext();) { - String key = (String) i.next(); - Object value = beans.get(key); - if (value != null) { - manager.declareBean(key, value, value.getClass()); - } else { - // BSF uses a hashtable to store values - // so cannot declareBean with a null value - // So need to remove any bean of this name as - // that bean should not be visible - manager.undeclareBean(key); - } - } - + BSFManager m = createManager(); + declareBeans(m); // execute the script - manager.exec(language, execName, 0, 0, script); - } catch (BSFException be) { - Throwable t = be; - Throwable te = be.getTargetException(); - if (te != null) { - if (te instanceof BuildException) { - throw (BuildException) te; - } else { - t = te; - } + if (engine == null) { + m.exec(getLanguage(), execName, 0, 0, getScript()); + } else { + engine.exec(execName, 0, 0, getScript()); } - throw new BuildException(t); + } catch (BSFException be) { + throwBuildException(be); } finally { - Thread.currentThread().setContextClassLoader( - origContextClassLoader); + restoreContextLoader(origLoader); } } /** - * Defines the language (required). - * - * @param language the scripting language name for the script. - */ - public void setLanguage(String language) { - this.language = language; - } - - /** - * Get the script language - * - * @return the script language - */ - public String getLanguage() { - return language; - } - - /** - * Set the class path to be used. - * @param classpath the path to use. - */ - public void setClasspath(Path classpath) { - this.classpath = classpath; - } - - /** - * Load the script from an external file ; optional. + * Do the work. * - * @param file the file containing the script source. + * @param execName the name that will be passed to BSF for this script + * execution. + * @return the result of the evalulation + * @exception BuildException if someting goes wrong exectuing the script. */ - public void setSrc(File file) { - if (!file.exists()) { - throw new BuildException("file " + file.getPath() + " not found."); - } - - int count = (int) file.length(); - byte[] data = new byte[count]; - FileInputStream inStream = null; + public Object evalulateScript(String execName) + throws BuildException { + checkLanguage(); + ClassLoader origLoader = replaceContextLoader(); try { - inStream = new FileInputStream(file); - inStream.read(data); - } catch (IOException e) { - throw new BuildException(e); + BSFManager m = createManager(); + declareBeans(m); + // execute the script + if (engine == null) { + return m.eval(getLanguage(), execName, 0, 0, getScript()); + } else { + return engine.eval(execName, 0, 0, getScript()); + } + } catch (BSFException be) { + throwBuildException(be); + // NotReached + return null; } finally { - FileUtils.close(inStream); + restoreContextLoader(origLoader); } - - script += new String(data); } /** - * Set the script text. - * - * @param text a component of the script text to be added. + * Throw a buildException in place of a BSFException. + * @param be BSFException to convert. + * @throws BuildException the conveted exception. */ - public void addText(String text) { - this.script += text; + private void throwBuildException(BSFException be) { + Throwable t = be; + Throwable te = be.getTargetException(); + if (te != null) { + if (te instanceof BuildException) { + throw (BuildException) te; + } else { + t = te; + } + } + throw new BuildException(t); } - /** - * Bind the runner to a project component. - * Properties, targets and references are all added as beans; - * project is bound to project, and self to the component. - * @param component to become <code>self</code> - */ - public void bindToComponent(ProjectComponent component) { - project = component.getProject(); - addBeans(project.getProperties()); - addBeans(project.getUserProperties()); - addBeans(project.getTargets()); - addBeans(project.getReferences()); - addBean("project", project); - addBean("self", component); + private void declareBeans(BSFManager m) throws BSFException { + for (Iterator i = getBeans().keySet().iterator(); i.hasNext();) { + String key = (String) i.next(); + Object value = getBeans().get(key); + if (value != null) { + m.declareBean(key, value, value.getClass()); + } else { + // BSF uses a hashtable to store values + // so cannot declareBean with a null value + // So need to remove any bean of this name as + // that bean should not be visible + m.undeclareBean(key); + } + } } - /** - * Bind the runner to a project component. - * The project and self are the only beans set. - * @param component to become <code>self</code> - */ - public void bindToComponentMinimum(ProjectComponent component) { - project = component.getProject(); - addBean("project", project); - addBean("self", component); + private BSFManager createManager() throws BSFException { + if (manager != null) { + return manager; + } + BSFManager m = new BSFManager(); + m.setClassLoader(getScriptClassLoader()); + if (getKeepEngine()) { + BSFEngine e = manager.loadScriptingEngine(getLanguage()); + this.manager = m; + this.engine = e; + } + return m; } } --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]