* 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.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 @@ public class Script extends Task { | |||
* @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 @@ public class Script extends Task { | |||
runner.addText(text); | |||
} | |||
if (classpath != null) { | |||
runner.setClasspath(classpath); | |||
runner.setScriptClassLoader( | |||
getProject().createClassLoader( | |||
getClass().getClassLoader(), classpath)); | |||
} | |||
if (setBeans) { | |||
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; | |||
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 @@ public class ScriptRunner { | |||
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. | |||
*/ | |||
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; | |||
} | |||
} |