diff options
Diffstat (limited to 'sca-java-1.x/tags/java-stable-20060304/sca/container.js/src/main/java/org/apache/tuscany/container/js/rhino/RhinoScript.java')
-rw-r--r-- | sca-java-1.x/tags/java-stable-20060304/sca/container.js/src/main/java/org/apache/tuscany/container/js/rhino/RhinoScript.java | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/sca-java-1.x/tags/java-stable-20060304/sca/container.js/src/main/java/org/apache/tuscany/container/js/rhino/RhinoScript.java b/sca-java-1.x/tags/java-stable-20060304/sca/container.js/src/main/java/org/apache/tuscany/container/js/rhino/RhinoScript.java new file mode 100644 index 0000000000..4ddee12fe4 --- /dev/null +++ b/sca-java-1.x/tags/java-stable-20060304/sca/container.js/src/main/java/org/apache/tuscany/container/js/rhino/RhinoScript.java @@ -0,0 +1,298 @@ +/** + * + * Copyright 2005 The Apache Software Foundation or its licensors, as applicable. + * + * Licensed 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.tuscany.container.js.rhino; + +import java.util.Iterator; +import java.util.Map; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Script; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.Wrapper; + +/** + * Represents, and is responsible for dispatching to, a JavaScript artifact in Rhino + */ +public class RhinoScript { + + private String scriptName; + + private String script; + + private Scriptable scriptScope; + + private Scriptable sharedScope; + + /* + * Enable dynamic scopes so a script can be used concurrently with a global shared scope and individual execution + * scopes. See http://www.mozilla.org/rhino/scopes.html TODO: need to review how ths fits in with Tuscany scopes + */ + private static class MyFactory extends ContextFactory { + protected boolean hasFeature(Context cx, int featureIndex) { + if (featureIndex == Context.FEATURE_DYNAMIC_SCOPE) { + return true; + } + return super.hasFeature(cx, featureIndex); + } + } + + static { + ContextFactory.initGlobal(new MyFactory()); + } + + /** + * Create a new RhinoInvoker. + * + * @param scriptName the name of the script. Can be anything, only used in messages to identify the script + * @param script the complete script + */ + public RhinoScript(String scriptName, String script) { + this(scriptName, script, (Map) null); + } + + /** + * Create a new RhinoInvoker. + * + * @param scriptName the name of the script. Can be anything, only used in messages to identify the script + * @param script the complete script + * @param context name-value pairs that are added in to the scope where the script is compiled. May be null. The + * value objects are made available to the script by using a variable with the name. + */ + public RhinoScript(String scriptName, String script, Map context) { + this.scriptName = scriptName; + this.script = script; + initScriptScope(scriptName, script, context); + initSharedScope(); + } + + /** + * Construct a RhinoInvoker from another RhinoInvoker object. This uses the original script scope so the script + * doesn't need to be recompiled. + */ + protected RhinoScript(String scriptName, String script, Scriptable scriptScope) { + this.scriptName = scriptName; + this.script = script; + this.scriptScope = scriptScope; + initSharedScope(); + } + + /** + * Invoke a script function + * + * @param functionName the name of the function to invoke. + * @param arg arguments to the function, may be a single object or an array of objects. + * @return the function return value. + */ + public Object invoke(String functionName, Object args) { + return invoke(functionName, args, null, null); + } + + /** + * Invoke a script function + * + * @param functionName the name of the function to invoke. + * @param arg arguments to the function, may be a single object or an array of objects. + * @param contexts a Map of name-value pairs which are added to the invocation Scope to enable the script to access + * the values by using the variable in name. + * @return the function return value. + */ + public Object invoke(String functionName, Object args, Map contexts) { + return invoke(functionName, args, null, contexts); + } + + /** + * Invoke a script function + * + * @param functionName the name of the function to invoke. + * @param arg arguments to the function, may be a single object or an array of objects. + * @param responseClass the desired class of the response object. + * @param contexts a Map of name-value pairs which are added to the invocation Scope to enable the script to access + * the values by using the variable in name. + * @return the function return value. + */ + public Object invoke(String functionName, Object arg, Class responseClass, Map contexts) { + Context cx = Context.enter(); + try { + Function function = getFunction(scriptScope, functionName); + Scriptable invocationScope = getInvocationScope(cx, contexts); + Object[] args = processArgs(arg, invocationScope); + Object jsResponse = function.call(cx, invocationScope, invocationScope, args); + Object response = processResponse(jsResponse, responseClass); + return response; + } finally { + Context.exit(); + } + } + + /** + * Turn args to JS objects and convert any OMElement to E4X XML + */ + protected Object[] processArgs(Object arg, Scriptable scope) { + // TODO: implement pluggable way to transform objects (eg SDO or AXIOM) to E4X XML objects + // if (arg instanceof OMElement) { + // try { + // arg = E4XAXIOMUtils.toScriptableObject((OMElement) arg, scope); + // } catch (XmlException e) { + // throw new RuntimeException(e); + // } + // } else if (arg instanceof MessageContext) { + // arg = new E4XMessageContext((MessageContext) arg, scope); + // } + Object[] args; + if (arg == null) { + args = new Object[] { null }; + } else if (arg.getClass().isArray()) { + args = (Object[]) arg; + for (int i = 0; i < args.length; i++) { + args[i] = Context.toObject(args[i], scope); + } + } else { + args = new Object[] { Context.toObject(arg, scope) }; + } + return args; + } + + /** + * Unwrap and convert response + */ + protected Object processResponse(Object response, Class responseClass) { + // TODO: implement pluggable way to transform E4X XML into specific objects (eg SDO or AXIOM) + // } else if (response instanceof XMLObject) { + // response = E4XAXIOMUtils.toOMElement((XMLObject) response); + if (Context.getUndefinedValue().equals(response)) { + response = null; + } else if (response instanceof Wrapper) { + response = ((Wrapper) response).unwrap(); + } else { + if (responseClass != null) { + response = Context.toType(response, responseClass); + } else { + response = Context.toType(response, String.class); + } + } + return response; + } + + /** + * Create a Rhino scope and compile the script into it + */ + protected void initScriptScope(String fileName, String scriptCode, Map context) { + Context cx = Context.enter(); + try { + + this.scriptScope = cx.initStandardObjects(null, true); + Script compiledScript = cx.compileString(scriptCode, fileName, 1, null); + compiledScript.exec(cx, scriptScope); + addContexts(scriptScope, context); + + } finally { + Context.exit(); + } + } + + /** + * Initializes the shared scope + */ + protected void initSharedScope() { + Context cx = Context.enter(); + try { + + this.sharedScope = cx.newObject(scriptScope); + sharedScope.setPrototype(scriptScope); + sharedScope.setParentScope(null); + + } finally { + Context.exit(); + } + } + + /** + * Get a Rhino scope for the function invocation. If the invocation has no context objects then this will use the + * shared scope otherwise a new scope is created to hold the context objects. Any new variables the executing script + * might define will go in the sharedScope. This new scope is just to hold the invocation specific context objects. + */ + protected Scriptable getInvocationScope(Context cx, Map contexts) { + + Scriptable scope; + if (contexts == null || contexts.size() == 0) { + scope = sharedScope; + } else { + scope = cx.newObject(sharedScope); + scope.setPrototype(sharedScope); + scope.setParentScope(null); + addContexts(scope, contexts); + } + + return scope; + } + + /** + * Add the context to the scope. This will make the objects available to a script by using the name it was added + * with. + */ + protected void addContexts(Scriptable scope, Map contexts) { + if (contexts != null) { + for (Iterator i = contexts.keySet().iterator(); i.hasNext();) { + String name = (String) i.next(); + Object value = contexts.get(name); + if (value != null) { + scope.put(name, scope, Context.toObject(value, scope)); + } + } + } + } + + /** + * Get the Rhino Function object for the named script function + */ + protected Function getFunction(Scriptable scope, String functionName) { + + Object handleObj = scope.get(functionName, scope); + + if (!(handleObj instanceof Function)) { + throw new RuntimeException("script function '" + functionName + "' is undefined or not a function in script " + + scriptName); + } + + return (Function) handleObj; + } + + /** + * Make a copy of this RhinoScript object. This shares the script scope to avoid the overhead of recompiling the + * script, and to allow any initialization done by the script to be shared. + */ + public RhinoScript copy() { + return new RhinoScript(scriptName, script, scriptScope); + } + + /** + * Update the scope where the script is complied with new context values + * + * @param properties + */ + public void updateScriptScope(Map context) { + Context.enter(); + try { + addContexts(scriptScope, context); + } finally { + Context.exit(); + } + } + +}
\ No newline at end of file |