001: /*
002: * $Id: OfbizJsBsfEngine.java,v 1.2 2003/08/19 21:14:51 jonesde Exp $
003: *
004: * Copyright (c) 2001, 2002 The Open For Business Project - www.ofbiz.org
005: *
006: * Permission is hereby granted, free of charge, to any person obtaining a
007: * copy of this software and associated documentation files (the "Software"),
008: * to deal in the Software without restriction, including without limitation
009: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
010: * and/or sell copies of the Software, and to permit persons to whom the
011: * Software is furnished to do so, subject to the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be included
014: * in all copies or substantial portions of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
017: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
018: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
019: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
020: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
021: * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
022: * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023: */
024: package org.ofbiz.base.util;
025:
026: import java.util.Vector;
027:
028: import org.mozilla.javascript.Context;
029: import org.mozilla.javascript.EvaluatorException;
030: import org.mozilla.javascript.JavaScriptException;
031: import org.mozilla.javascript.NativeJavaObject;
032: import org.mozilla.javascript.Scriptable;
033: import org.mozilla.javascript.ScriptRuntime;
034: import org.mozilla.javascript.WrappedException;
035: import org.mozilla.javascript.Wrapper;
036: import org.mozilla.javascript.ImporterTopLevel;
037:
038: import com.ibm.bsf.*;
039: import com.ibm.bsf.util.BSFEngineImpl;
040: import com.ibm.bsf.util.BSFFunctions;
041:
042: /**
043: * This is the interface to Netscape's Rhino (JavaScript) from the
044: * Bean Scripting Framework.
045: * <p>
046: * The original version of this code was first written by Adam Peller
047: * for use in LotusXSL. Sanjiva took his code and adapted it for BSF.
048: *
049: * <p>Also modified for optimized use in the OFBiz framework.
050: *
051: * @author Adam Peller <peller@lotus.com>
052: * @author Sanjiva Weerawarana
053: * @author Matthew J. Duftler
054: * @author Norris Boyd
055: * @author David E. Jones <jonesde@ofbiz.org>
056: */
057: public class OfbizJsBsfEngine extends BSFEngineImpl {
058:
059: public static final String module = OfbizJsBsfEngine.class
060: .getName();
061:
062: /**
063: * The global script object, where all embedded functions are defined,
064: * as well as the standard ECMA "core" objects.
065: */
066: private Scriptable global;
067:
068: /**
069: * initialize the engine. put the manager into the context -> manager
070: * map hashtable too.
071: */
072: public void initialize(BSFManager mgr, String lang,
073: Vector declaredBeans) throws BSFException {
074: super .initialize(mgr, lang, declaredBeans);
075:
076: // Initialize context and global scope object
077: try {
078: Context cx = Context.enter();
079: global = cx.initStandardObjects(new ImporterTopLevel(cx));
080: Scriptable bsf = Context.toObject(new BSFFunctions(mgr,
081: this ), global);
082: global.put("bsf", global, bsf);
083:
084: int size = declaredBeans.size();
085: for (int i = 0; i < size; i++) {
086: declareBean((BSFDeclaredBean) declaredBeans
087: .elementAt(i));
088: }
089: } finally {
090: Context.exit();
091: }
092: }
093:
094: /**
095: * This is used by an application to evaluate a string containing
096: * some expression.
097: */
098: public Object eval(String source, int lineNo, int columnNo,
099: Object oscript) throws BSFException {
100: if (Debug.verboseOn())
101: Debug.logVerbose("Running javascript script " + source
102: + " through OFBiz BSH engine", module);
103: String script = oscript.toString();
104: Object retval = null;
105: try {
106: Context cx = Context.enter();
107:
108: // Use interpretive mode (-1) --generally faster for single executions of scripts.
109: // Use optimized/compiled mode (9) --generally faster for repeated executions of scripts.
110: cx.setOptimizationLevel(9);
111:
112: retval = cx.evaluateString(global, script, source, lineNo,
113: null);
114: if (retval instanceof NativeJavaObject)
115: retval = ((NativeJavaObject) retval).unwrap();
116: } catch (Throwable t) { // includes JavaScriptException, rethrows Errors
117: handleError(t);
118: } finally {
119: Context.exit();
120: }
121: return retval;
122: }
123:
124: /**
125: * Return an object from an extension.
126: * @param object Object on which to make the call (ignored).
127: * @param method The name of the method to call.
128: * @param args an array of arguments to be
129: * passed to the extension, which may be either
130: * Vectors of Nodes, or Strings.
131: */
132: public Object call(Object object, String method, Object[] args)
133: throws BSFException {
134: Object theReturnValue = null;
135:
136: try {
137: Context cx = Context.enter();
138:
139: //REMIND: convert arg list Vectors here?
140:
141: Object fun = global.get(method, global);
142: if (fun == Scriptable.NOT_FOUND) {
143: throw new JavaScriptException("function " + method
144: + " not found.");
145: }
146:
147: theReturnValue = ScriptRuntime.call(cx, fun, global, args,
148: null);
149: if (theReturnValue instanceof Wrapper) {
150: theReturnValue = ((Wrapper) theReturnValue).unwrap();
151: }
152: } catch (Throwable t) {
153: handleError(t);
154: } finally {
155: Context.exit();
156: }
157: return theReturnValue;
158: }
159:
160: public void declareBean(BSFDeclaredBean bean) throws BSFException {
161: // Must wrap non-scriptable objects before presenting to Rhino
162: Scriptable wrapped = Context.toObject(bean.bean, global);
163: global.put(bean.name, global, wrapped);
164: }
165:
166: public void undeclareBean(BSFDeclaredBean bean) throws BSFException {
167: global.delete(bean.name);
168: }
169:
170: private void handleError(Throwable t) throws BSFException {
171: if (t instanceof WrappedException) {
172: t = (Throwable) ((WrappedException) t).unwrap();
173: }
174:
175: String message = null;
176: Throwable target = t;
177:
178: if (t instanceof JavaScriptException) {
179: message = t.getLocalizedMessage();
180:
181: // Is it an exception wrapped in a JavaScriptException?
182: Object value = ((JavaScriptException) t).getValue();
183: if (value instanceof Throwable) {
184: // likely a wrapped exception from a LiveConnect call.
185: // Display its stack trace as a diagnostic
186: target = (Throwable) value;
187: }
188: } else if (t instanceof EvaluatorException
189: || t instanceof SecurityException) {
190: message = t.getLocalizedMessage();
191: } else if (t instanceof RuntimeException) {
192: message = "Internal Error: " + t.toString();
193: } else if (t instanceof StackOverflowError) {
194: message = "Stack Overflow";
195: }
196:
197: if (message == null) {
198: message = t.toString();
199: }
200:
201: //REMIND: can we recover the line number here? I think
202: // Rhino does this by looking up the stack for bytecode
203: // see Context.getSourcePositionFromStack()
204: // but I don't think this would work in interpreted mode
205:
206: if (t instanceof Error && !(t instanceof StackOverflowError)) {
207: // Re-throw Errors because we're supposed to let the JVM see it
208: // Don't re-throw StackOverflows, because we know we've
209: // corrected the situation by aborting the loop and
210: // a long stacktrace would end up on the user's console
211: throw (Error) t;
212: } else {
213: throw new BSFException(BSFException.REASON_OTHER_ERROR,
214: "JavaScript Error: " + message, target);
215: }
216: }
217: }
|