Browse Source

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)
    


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@478700 13f79535-47bb-0310-9956-ffa450edef68
master
Peter Reilly 18 years ago
parent
commit
c0b5350981
3 changed files with 386 additions and 181 deletions
  1. +5
    -2
      src/main/org/apache/tools/ant/taskdefs/optional/Script.java
  2. +283
    -0
      src/main/org/apache/tools/ant/util/ScriptRunnerBase.java
  3. +98
    -179
      src/main/org/apache/tools/ant/util/optional/ScriptRunner.java

+ 5
- 2
src/main/org/apache/tools/ant/taskdefs/optional/Script.java View File

@@ -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);


+ 283
- 0
src/main/org/apache/tools/ant/util/ScriptRunnerBase.java View File

@@ -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);
}
}

+ 98
- 179
src/main/org/apache/tools/ant/util/optional/ScriptRunner.java View File

@@ -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;
}
}

Loading…
Cancel
Save