* 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) git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@478700 13f79535-47bb-0310-9956-ffa450edef68master
@@ -21,6 +21,7 @@ import java.io.File; | |||||
import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
import org.apache.tools.ant.Task; | import org.apache.tools.ant.Task; | ||||
import org.apache.tools.ant.util.optional.ScriptRunner; | 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.Path; | ||||
import org.apache.tools.ant.types.Reference; | import org.apache.tools.ant.types.Reference; | ||||
@@ -43,7 +44,7 @@ public class Script extends Task { | |||||
* @exception BuildException if something goes wrong with the build | * @exception BuildException if something goes wrong with the build | ||||
*/ | */ | ||||
public void execute() throws BuildException { | public void execute() throws BuildException { | ||||
ScriptRunner runner = new ScriptRunner(); | |||||
ScriptRunnerBase runner = new ScriptRunner(); | |||||
if (language != null) { | if (language != null) { | ||||
runner.setLanguage(language); | runner.setLanguage(language); | ||||
} | } | ||||
@@ -54,7 +55,9 @@ public class Script extends Task { | |||||
runner.addText(text); | runner.addText(text); | ||||
} | } | ||||
if (classpath != null) { | if (classpath != null) { | ||||
runner.setClasspath(classpath); | |||||
runner.setScriptClassLoader( | |||||
getProject().createClassLoader( | |||||
getClass().getClassLoader(), classpath)); | |||||
} | } | ||||
if (setBeans) { | if (setBeans) { | ||||
runner.bindToComponent(this); | runner.bindToComponent(this); | ||||
@@ -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); | |||||
} | |||||
} |
@@ -17,35 +17,22 @@ | |||||
*/ | */ | ||||
package org.apache.tools.ant.util.optional; | 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.BSFException; | ||||
import org.apache.bsf.BSFManager; | import org.apache.bsf.BSFManager; | ||||
import org.apache.bsf.BSFEngine; | |||||
import org.apache.tools.ant.BuildException; | 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 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 | * 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 { | static { | ||||
BSFManager.registerScriptingEngine( | BSFManager.registerScriptingEngine( | ||||
"groovy", | "groovy", | ||||
@@ -53,58 +40,30 @@ public class ScriptRunner { | |||||
new String[] {"groovy", "gy"}); | 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; | |||||
/** | |||||
* 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 | |||||
} | |||||
} | |||||
} | |||||
private BSFEngine engine; | |||||
private BSFManager manager; | |||||
/** | /** | ||||
* 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. | |||||
* Check if bsf supports the language. | |||||
* @return true if bsf can create an engine for this language. | |||||
*/ | */ | ||||
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)); | |||||
public boolean supportsLanguage() { | |||||
if (manager != null) { | |||||
return true; | |||||
} | } | ||||
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 @@ public class ScriptRunner { | |||||
* @exception BuildException if someting goes wrong exectuing the script. | * @exception BuildException if someting goes wrong exectuing the script. | ||||
*/ | */ | ||||
public void executeScript(String execName) throws BuildException { | 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 { | 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 | // 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 { | } 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 { | 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 { | } 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; | |||||
} | } | ||||
} | } |