You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by ad...@apache.org on 2012/03/05 00:43:28 UTC
svn commit: r1296897 - in /ofbiz/trunk/framework/base:
config/cache.properties src/org/ofbiz/base/util/ScriptUtil.java
Author: adrianc
Date: Sun Mar 4 23:43:27 2012
New Revision: 1296897
URL: http://svn.apache.org/viewvc?rev=1296897&view=rev
Log:
Refactored ScriptUtil.java to use the javax.script package (JSR-223).
Modified:
ofbiz/trunk/framework/base/config/cache.properties
ofbiz/trunk/framework/base/src/org/ofbiz/base/util/ScriptUtil.java
Modified: ofbiz/trunk/framework/base/config/cache.properties
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/config/cache.properties?rev=1296897&r1=1296896&r2=1296897&view=diff
==============================================================================
--- ofbiz/trunk/framework/base/config/cache.properties (original)
+++ ofbiz/trunk/framework/base/config/cache.properties Sun Mar 4 23:43:27 2012
@@ -70,7 +70,7 @@ minilang.SimpleMethodsURL.expireTime=100
script.BshLocationParsedCache.expireTime=10000
script.BshBsfParsedCache.expireTime=10000
-
+script.ParsedScripts.expireTime=10000
script.GroovyLocationParsedCache.expireTime=10000
# Uncomment this for more flexibility with service changes.
Modified: ofbiz/trunk/framework/base/src/org/ofbiz/base/util/ScriptUtil.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/src/org/ofbiz/base/util/ScriptUtil.java?rev=1296897&r1=1296896&r2=1296897&view=diff
==============================================================================
--- ofbiz/trunk/framework/base/src/org/ofbiz/base/util/ScriptUtil.java (original)
+++ ofbiz/trunk/framework/base/src/org/ofbiz/base/util/ScriptUtil.java Sun Mar 4 23:43:27 2012
@@ -18,77 +18,255 @@
*******************************************************************************/
package org.ofbiz.base.util;
-import org.codehaus.groovy.runtime.InvokerHelper;
-
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.util.Map;
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import javax.script.SimpleScriptContext;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.ofbiz.base.location.FlexibleLocation;
+import org.ofbiz.base.util.cache.UtilCache;
+
+/**
+ * Scripting utility methods. This is a facade class that is used to connect OFBiz to JSR-223 scripting engines.
+ *
+ */
public final class ScriptUtil {
public static final String module = ScriptUtil.class.getName();
+ private static UtilCache<String, CompiledScript> parsedScripts = UtilCache.createUtilCache("script.ParsedScripts", 0, 0, false);
private static final Object[] EMPTY_ARGS = {};
- /* TODO: the "evaluate" and "executeScript" method method could be enhanced to implement JSR-223 using code like:
- import javax.script.ScriptEngineManager;
- import javax.script.ScriptEngine;
- ...
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine scriptEngine = manager.getEngineByExtension(location.substring(location.indexOf(".") + 1));
- ...
- Object result = scriptEngine.eval(scriptFileReader, scriptContext);
-
- However it may make more sense to keep a custom way to load and execute Groovy scripts and implement JSR-223
- for the other scripting languages: in this way the OFBiz framework will support any script language with JSR-223
- but will still have specialized support for Groovy (where we could/should inject OFBiz specific utility methods
- and create a security sandbox for Groovy dynamic code).
- */
- public static Object evaluate(String language, String script, Class<?> scriptClass, Map inputMap) throws Exception {
- /*
- TODO: for JSR-223 we could use:
- ScriptEngine scriptEngine = manager.getEngineByName(location);
- */
- Object result = null;
- if ("groovy".equals(language)) {
- if (scriptClass == null) {
- scriptClass = ScriptUtil.parseScript(language, script);
+ /**
+ * Returns a compiled script.
+ *
+ * @param filePath Script path and file name.
+ * @return The compiled script, or <code>null</code> if the script engine does not support compilation.
+ * @throws FileNotFoundException
+ * @throws IllegalArgumentException
+ * @throws ScriptException
+ * @throws MalformedURLException
+ */
+ public static CompiledScript compileScriptFile(String filePath) throws FileNotFoundException, ScriptException, MalformedURLException {
+ Assert.notNull("filePath", filePath);
+ CompiledScript script = parsedScripts.get(filePath);
+ if (script == null) {
+ ScriptEngineManager manager = new ScriptEngineManager();
+ ScriptEngine engine = manager.getEngineByExtension(getFileExtension(filePath));
+ if (engine == null) {
+ throw new IllegalArgumentException("The script type is not supported for location: " + filePath);
}
- if (scriptClass != null) {
- result = InvokerHelper.createScript(scriptClass, GroovyUtil.getBinding(inputMap)).run();
+ try {
+ Compilable compilableEngine = (Compilable) engine;
+ URL scriptUrl = FlexibleLocation.resolveLocation(filePath);
+ FileReader reader = new FileReader(new File(scriptUrl.getFile()));
+ script = compilableEngine.compile(reader);
+ if (Debug.verboseOn()) {
+ Debug.logVerbose("Compiled script " + filePath + " using engine " + engine.getClass().getName(), module);
+ }
+ } catch (ClassCastException e) {
+ if (Debug.verboseOn()) {
+ Debug.logVerbose("Script engine " + engine.getClass().getName() + " does not implement Compilable", module);
+ }
+ }
+ if (script != null) {
+ parsedScripts.putIfAbsent(filePath, script);
}
- } else if ("bsh".equals(language)) {
- result = BshUtil.eval(script, UtilMisc.makeMapWritable(inputMap));
}
- return result;
+ return script;
}
- public static void executeScript(String location, String method, Map<String, Object> context) {
- /*
- TODO: for JSR-223 we could use:
- ScriptEngine scriptEngine = manager.getEngineByExtension(location.substring(location.indexOf(".") + 1));
- */
- if (location.endsWith(".bsh")) {
+ /**
+ * Returns a compiled script.
+ *
+ * @param language
+ * @param script
+ * @return The compiled script, or <code>null</code> if the script engine does not support compilation.
+ * @throws IllegalArgumentException
+ * @throws ScriptException
+ */
+ public static CompiledScript compileScriptString(String language, String script) throws ScriptException {
+ Assert.notNull("language", language, "script", script);
+ String cacheKey = language.concat("://").concat(script);
+ CompiledScript compiledScript = parsedScripts.get(cacheKey);
+ if (compiledScript == null) {
+ ScriptEngineManager manager = new ScriptEngineManager();
+ ScriptEngine engine = manager.getEngineByName(language);
+ if (engine == null) {
+ throw new IllegalArgumentException("The script type is not supported for language: " + language);
+ }
try {
- BshUtil.runBshAtLocation(location, context);
- } catch (GeneralException e) {
- String errMsg = "Error running BSH script at location [" + location + "]: " + e.toString();
- Debug.logError(e, errMsg, module);
- throw new IllegalArgumentException(errMsg);
+ Compilable compilableEngine = (Compilable) engine;
+ compiledScript = compilableEngine.compile(script);
+ if (Debug.verboseOn()) {
+ Debug.logVerbose("Compiled script [" + script + "] using engine " + engine.getClass().getName(), module);
+ }
+ } catch (ClassCastException e) {
+ if (Debug.verboseOn()) {
+ Debug.logVerbose("Script engine " + engine.getClass().getName() + " does not implement Compilable", module);
+ }
}
- } else if (location.endsWith(".groovy")) {
+ if (script != null) {
+ parsedScripts.putIfAbsent(cacheKey, compiledScript);
+ }
+ }
+ return compiledScript;
+ }
+
+ /**
+ * Returns a <code>ScriptContext</code> that contains the members of <code>context</code>.
+ * <p>If a <code>CompiledScript</code> instance is to be shared by multiple threads, then
+ * each thread must create its own <code>ScriptContext</code> and pass it to the
+ * <code>CompiledScript</code> eval method.</p>
+ *
+ * @param context
+ * @return
+ */
+ public static ScriptContext createScriptContext(Map<String, ? extends Object> context) {
+ ScriptContext scriptContext = new SimpleScriptContext();
+ Bindings bindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
+ bindings.putAll(context);
+ bindings.put("context", context);
+ return scriptContext;
+ }
+
+ /**
+ * Executes a script <code>String</code> and returns the result.
+ *
+ * @param language
+ * @param script
+ * @param scriptClass
+ * @param inputMap
+ * @return The script result.
+ * @throws Exception
+ */
+ public static Object evaluate(String language, String script, Class<?> scriptClass, Map<String, ? extends Object> inputMap) throws Exception {
+ Assert.notNull("inputMap", inputMap);
+ if (scriptClass != null) {
+ return InvokerHelper.createScript(scriptClass, GroovyUtil.getBinding(inputMap)).run();
+ }
+ // TODO: Remove beanshell check when all beanshell code has been removed.
+ if ("bsh".equals(language)) {
+ return BshUtil.eval(script, UtilMisc.makeMapWritable(inputMap));
+ }
+ try {
+ CompiledScript compiledScript = compileScriptString(language, script);
+ if (compiledScript != null) {
+ return executeScript(compiledScript, null, inputMap);
+ }
+ ScriptEngineManager manager = new ScriptEngineManager();
+ ScriptEngine engine = manager.getEngineByName(language);
+ if (engine == null) {
+ throw new IllegalArgumentException("The script type is not supported for language: " + language);
+ }
+ if (Debug.verboseOn()) {
+ Debug.logVerbose("Begin processing script [" + script + "] using engine " + engine.getClass().getName(), module);
+ }
+ ScriptContext scriptContext = createScriptContext(inputMap);
+ return engine.eval(script, scriptContext);
+ } catch (Exception e) {
+ String errMsg = "Error running " + language + " script [" + script + "]: " + e.toString();
+ Debug.logWarning(e, errMsg, module);
+ throw new IllegalArgumentException(errMsg);
+ }
+ }
+
+ /**
+ * Executes a compiled script and returns the result.
+ *
+ * @param script Compiled script.
+ * @param functionName Optional function or method to invoke.
+ * @param context Script execution context.
+ * @return The script result.
+ * @throws IllegalArgumentException
+ */
+ public static Object executeScript(CompiledScript script, String functionName, Map<String, ? extends Object> context) throws ScriptException, NoSuchMethodException {
+ Assert.notNull("script", script, "context", context);
+ ScriptContext scriptContext = createScriptContext(context);
+ Object result = script.eval(scriptContext);
+ if (UtilValidate.isNotEmpty(functionName)) {
+ if (Debug.verboseOn()) {
+ Debug.logVerbose("Invoking function/method " + functionName, module);
+ }
+ ScriptEngine engine = script.getEngine();
try {
- groovy.lang.Script script = InvokerHelper.createScript(GroovyUtil.getScriptClassFromLocation(location), GroovyUtil.getBinding(context));
- if (UtilValidate.isEmpty(method)) {
- script.run();
- } else {
- script.invokeMethod(method, EMPTY_ARGS);
- }
- } catch (GeneralException e) {
- String errMsg = "Error running Groovy script at location [" + location + "]: " + e.toString();
- Debug.logError(e, errMsg, module);
- throw new IllegalArgumentException(errMsg);
+ Invocable invocableEngine = (Invocable) engine;
+ result = invocableEngine.invokeFunction(functionName, EMPTY_ARGS);
+ } catch (ClassCastException e) {
+ throw new ScriptException("Script engine " + engine.getClass().getName() + " does not support function/method invocations");
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Executes the script at the specified location and returns the result.
+ *
+ * @param filePath Script path and file name.
+ * @param functionName Optional function or method to invoke.
+ * @param context Script execution context.
+ * @return The script result.
+ * @throws IllegalArgumentException
+ */
+ public static Object executeScript(String filePath, String functionName, Map<String, ? extends Object> context) {
+ Assert.notNull("filePath", filePath, "context", context);
+ try {
+ CompiledScript script = compileScriptFile(filePath);
+ if (script != null) {
+ return executeScript(script, functionName, context);
+ }
+ String fileExtension = getFileExtension(filePath);
+ // TODO: Remove beanshell check when all beanshell code has been removed.
+ if ("bsh".equals(fileExtension)) {
+ return BshUtil.runBshAtLocation(filePath, context);
+ } else {
+ ScriptEngineManager manager = new ScriptEngineManager();
+ ScriptEngine engine = manager.getEngineByExtension(fileExtension);
+ if (engine == null) {
+ throw new IllegalArgumentException("The script type is not supported for location: " + filePath);
+ }
+ if (Debug.verboseOn()) {
+ Debug.logVerbose("Begin processing script [" + script + "] using engine " + engine.getClass().getName(), module);
+ }
+ ScriptContext scriptContext = createScriptContext(context);
+ URL scriptUrl = FlexibleLocation.resolveLocation(filePath);
+ FileReader reader = new FileReader(new File(scriptUrl.getFile()));
+ Object result = engine.eval(reader, scriptContext);
+ if (UtilValidate.isNotEmpty(functionName)) {
+ try {
+ Invocable invocableEngine = (Invocable) engine;
+ result = invocableEngine.invokeFunction(functionName, EMPTY_ARGS);
+ } catch (ClassCastException e) {
+ throw new ScriptException("Script engine " + engine.getClass().getName() + " does not support function/method invocations");
+ }
+ }
+ return result;
}
- } else {
- throw new IllegalArgumentException("The script type is not yet support for location:" + location);
+ } catch (Exception e) {
+ String errMsg = "Error running script at location [" + filePath + "]: " + e.toString();
+ Debug.logWarning(e, errMsg, module);
+ throw new IllegalArgumentException(errMsg);
+ }
+ }
+
+ private static String getFileExtension(String filePath) {
+ int pos = filePath.lastIndexOf(".");
+ if (pos == -1) {
+ throw new IllegalArgumentException("Extension missing in script file name: " + filePath);
}
+ return filePath.substring(pos + 1);
}
public static Class<?> parseScript(String language, String script) {