1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.struts.flow.core.javascript.fom;
17
18 import java.awt.Dimension;
19 import java.awt.Toolkit;
20 import java.io.BufferedReader;
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.io.OutputStream;
24 import java.io.PushbackInputStream;
25 import java.io.Reader;
26 import java.util.ArrayList;
27 import java.util.*;
28 import java.util.Map;
29
30 import org.apache.struts.flow.core.Logger;
31 import org.apache.struts.flow.core.Factory;
32
33 import org.apache.commons.chain.web.WebContext;
34 import java.lang.reflect.InvocationTargetException;
35
36
37
38
39
40
41
42 import org.apache.struts.flow.core.CompilingInterpreter;
43 import org.apache.struts.flow.core.Interpreter;
44 import org.apache.struts.flow.core.InvalidContinuationException;
45 import org.apache.struts.flow.core.*;
46 import org.apache.struts.flow.core.javascript.JSErrorReporter;
47 import org.apache.struts.flow.core.javascript.LocationTrackingDebugger;
48
49
50
51
52
53
54
55
56 import org.apache.struts.flow.core.source.Source;
57
58
59
60 import org.mozilla.javascript.Context;
61 import org.mozilla.javascript.EcmaError;
62 import org.mozilla.javascript.Function;
63 import org.mozilla.javascript.JavaScriptException;
64 import org.mozilla.javascript.NativeJavaClass;
65 import org.mozilla.javascript.NativeJavaPackage;
66 import org.mozilla.javascript.PropertyException;
67 import org.mozilla.javascript.Script;
68 import org.mozilla.javascript.ScriptRuntime;
69 import org.mozilla.javascript.Scriptable;
70 import org.mozilla.javascript.ScriptableObject;
71 import org.mozilla.javascript.WrappedException;
72 import org.mozilla.javascript.Wrapper;
73 import org.mozilla.javascript.WrapFactory;
74 import org.mozilla.javascript.continuations.Continuation;
75 import org.mozilla.javascript.tools.debugger.Main;
76 import org.mozilla.javascript.tools.shell.Global;
77
78 import java.util.regex.Pattern;
79 import java.util.regex.Matcher;
80
81 /***
82 * Interface with the JavaScript interpreter.
83 *
84 * @author <a href="mailto:ovidiu@apache.org">Ovidiu Predescu</a>
85 * @author <a href="mailto:crafterm@apache.org">Marcus Crafter</a>
86 * @since March 25, 2002
87 * @version CVS $Id: FOM_JavaScriptInterpreter.java 307410 2005-10-09 12:17:33Z reinhard $
88 */
89 public class FOM_JavaScriptInterpreter extends CompilingInterpreter {
90
91 /***
92 * A long value is stored under this key in each top level JavaScript
93 * thread scope object. When you enter a context any scripts whose
94 * modification time is later than this value will be recompiled and reexecuted,
95 * and this value will be updated to the current time.
96 */
97 private final static String LAST_EXEC_TIME = "__PRIVATE_LAST_EXEC_TIME__";
98
99 /***
100 * Prefix for session/request attribute storing JavaScript global scope object.
101 */
102 private static final String USER_GLOBAL_SCOPE = "FOM JavaScript GLOBAL SCOPE/";
103
104 /***
105 * This is the only optimization level that supports continuations
106 * in the Christoper Oliver's Rhino JavaScript implementation
107 */
108 private static final int OPTIMIZATION_LEVEL = -2;
109
110 /***
111 * When was the last time we checked for script modifications. Used
112 * only if {@link #reloadScripts} is true.
113 */
114 private long lastTimeCheck;
115
116 /***
117 * Shared global scope for scripts and other immutable objects
118 */
119 private Global scope;
120
121 /***
122 * List of <code>String</code> objects that represent files to be
123 * read in by the JavaScript interpreter.
124 */
125 private List topLevelScripts = new ArrayList();
126
127 private boolean enableDebugger;
128
129 private WrapFactory wrapFactory;
130 private Map flowVars = new HashMap();
131 private Map globalVars = new HashMap();
132
133 /***
134 * JavaScript debugger: there's only one of these: it can debug multiple
135 * threads executing JS code.
136 */
137 private static Main debugger;
138
139 static synchronized Main getDebugger() {
140 if (debugger == null) {
141 final Main db = new Main("Flow Debugger");
142 db.pack();
143 Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
144 size.width *= 0.75;
145 size.height *= 0.75;
146 db.setSize(size);
147 db.setExitAction(new Runnable() {
148 public void run() {
149 db.setVisible(false);
150 }
151 });
152 db.setOptimizationLevel(OPTIMIZATION_LEVEL);
153 db.setVisible(true);
154 debugger = db;
155 Context.addContextListener(debugger);
156 }
157 return debugger;
158 }
159
160 /***
161 * Gets the logger attribute of the JavaScriptInterpreter object
162 *
163 *@return The logger value
164 */
165 public Logger getLogger() {
166 return Factory.getLogger();
167 }
168
169 /***
170 * Sets the wrap factory to use with Rhino
171 *
172 *@param wf The WrapFactory instance
173 */
174 public void setWrapFactory(WrapFactory wf) {
175 this.wrapFactory = wf;
176 }
177
178 /***
179 * Adds a class that will register a global variable
180 *
181 * @param reg The variable registrar
182 */
183 public void addGlobalVariable(String name, Object var) {
184 globalVars.put(name, var);
185 }
186
187 /***
188 * Removes a class that will register a global variable
189 *
190 * @param reg The variable registrar
191 */
192 public void removeGlobalVariable(String name) {
193 globalVars.remove(name);
194 }
195
196 /***
197 * Adds a class that will register a global variable
198 *
199 * @param reg The variable registrar
200 */
201 public void addFlowVariable(String name, FlowVariableFactory fac) {
202 flowVars.put(name, fac);
203 }
204
205 /***
206 * Removes a class that will register a global variable
207 *
208 * @param reg The variable registrar
209 */
210 public void removeFlowVariable(String name) {
211 flowVars.remove(name);
212 }
213
214
215 /***
216 * Sets the interval between when the script should be looked at to see if
217 * it needs to be reloaded
218 *
219 *@param time The interval time in milliseconds
220 */
221 public void setCheckTime(long time) {
222 checkTime = time;
223 }
224
225
226 /***
227 * Sets whether to enable the debugger
228 *
229 *@param val The new debugger value
230 */
231 public void setDebugger(boolean val) {
232 enableDebugger = val;
233 }
234
235
236 /***
237 * Sets whether to try to reload modified scripts or not
238 *
239 *@param val True to reload
240 */
241 public void setReloadScripts(boolean val) {
242 reloadScripts = val;
243 }
244
245 /***
246 * Initialize the global scope
247 *
248 *@exception FlowException If anything goes wrong
249 */
250 public void initialize() throws FlowException {
251 initialize(null);
252 }
253
254
255 /***
256 * Initialize the global scope
257 *
258 *@exception FlowException If anything goes wrong
259 */
260 public void initialize(List classes) throws FlowException {
261 if (enableDebugger) {
262 if (getLogger().isDebugEnabled()) {
263 getLogger().debug("Flow debugger enabled, creating");
264 }
265 getDebugger().doBreak();
266 }
267 Context context = Context.enter();
268 context.setOptimizationLevel(OPTIMIZATION_LEVEL);
269 context.setCompileFunctionsWithDynamicScope(true);
270 context.setGeneratingDebug(true);
271 if (wrapFactory != null) {
272 context.setWrapFactory(wrapFactory);
273 }
274
275 try {
276 scope = new Global(context);
277
278
279
280 initScope(context, scope);
281
282
283 if (classes != null) {
284 for (Iterator i = classes.iterator(); i.hasNext(); ) {
285 ScriptableObject.defineClass(scope, (Class)i.next());
286 }
287 }
288
289
290 FOM_Flow.init(scope);
291 } catch (Exception e) {
292 Context.exit();
293 e.printStackTrace();
294 throw new FlowException(e);
295 }
296 }
297
298
299 /***
300 * Initialize the global scope
301 *
302 *@param context The context
303 *@param scope The scope to initialize
304 *@exception IllegalAccessException If anything goes wrong
305 *@exception InstantiationException If anything goes wrong
306 *@exception InvocationTargetException If anything goes wrong
307 *@exception JavaScriptException If anything goes wrong
308 */
309 protected void initScope(Context context, Global scope) throws IllegalAccessException,
310 InstantiationException, InvocationTargetException, JavaScriptException {
311
312 WrapFactory factory = context.getWrapFactory();
313 for (Iterator i = globalVars.keySet().iterator(); i.hasNext(); ) {
314 String name = (String) i.next();
315 Object bean = globalVars.get(name);
316
317 if (bean instanceof Scriptable) {
318 scope.put(name, scope, (Scriptable)bean);
319 } else {
320 Scriptable var = factory.wrapAsJavaObject(context, scope, bean, bean.getClass());
321 scope.put(name, scope, var);
322 }
323 }
324 }
325
326
327 /***
328 * Returns the JavaScript scope, a Scriptable object, from the user
329 * session instance. Each interpreter instance can have a scope
330 * associated with it.
331 *
332 * @return a <code>ThreadScope</code> value
333 */
334 private ThreadScope getSessionScope(WebContext ctx) throws Exception {
335 final String scopeID = USER_GLOBAL_SCOPE + getInterpreterID();
336
337 ThreadScope scope = null;
338
339
340
341
342
343
344
345
346
347
348 scope = (ThreadScope) ctx.getSessionScope().get(scopeID);
349 if (scope == null) {
350 scope = (ThreadScope) ctx.getRequestScope().get(scopeID);
351 }
352
353
354 if (scope == null) {
355 scope = createThreadScope();
356
357 ctx.getRequestScope().put(scopeID, scope);
358 }
359
360 return scope;
361 }
362
363 /***
364 * Associates a JavaScript scope, a Scriptable object, with
365 * {@link #getInterpreterID() identifier} of this {@link Interpreter}
366 * instance.
367 *
368 * @param scope a <code>ThreadScope</code> value
369 */
370 private void setSessionScope(ThreadScope scope, WebContext ctx) throws Exception {
371 if (scope.useSession) {
372 final String scopeID = USER_GLOBAL_SCOPE + getInterpreterID();
373
374
375
376 try {
377 ctx.getSessionScope().put(scopeID, scope);
378 } catch (IllegalStateException e) {
379
380 if (getLogger().isDebugEnabled()) {
381 getLogger().debug("Got '" + e + "' while trying to set session scope.", e);
382 }
383 }
384 }
385 }
386
387 public static class ThreadScope extends ScriptableObject {
388 private static final String[] BUILTIN_PACKAGES = {"javax", "org", "com"};
389
390 private ClassLoader classLoader;
391
392
393 boolean useSession;
394
395 boolean locked = false;
396
397 /***
398 * Initializes new top-level scope.
399 */
400 public ThreadScope(Global scope, Map flowVars, WrapFactory wrapFactory) throws Exception {
401 final Context context = Context.getCurrentContext();
402 if (wrapFactory != null) {
403 context.setWrapFactory(wrapFactory);
404 }
405
406 final String[] names = { "importClass" };
407 try {
408 defineFunctionProperties(names,
409 ThreadScope.class,
410 ScriptableObject.DONTENUM);
411 } catch (PropertyException e) {
412 throw new Error();
413 }
414
415 setPrototype(scope);
416
417
418
419
420 setParentScope(null);
421
422
423
424
425 final Object[] args = {};
426 FOM_Flow flow = (FOM_Flow) context.newObject(this,
427 "FOM_Flow",
428 args);
429 flow.setParentScope(this);
430 super.put("flow", this, flow);
431
432 WrapFactory factory = context.getWrapFactory();
433 for (Iterator i = flowVars.keySet().iterator(); i.hasNext(); ) {
434 String name = (String) i.next();
435 FlowVariableFactory varfactory = (FlowVariableFactory) flowVars.get(name);
436
437 Object bean = varfactory.getInstance(this, flow);
438
439 if (bean instanceof Scriptable) {
440 super.put(name, this, (Scriptable) bean);
441 } else {
442 Scriptable var = (Scriptable) factory.wrapAsJavaObject(context, this, bean, bean.getClass());
443 super.put(name, this, var);
444 }
445 }
446
447 defineProperty(LAST_EXEC_TIME,
448 new Long(0),
449 ScriptableObject.DONTENUM | ScriptableObject.PERMANENT);
450 }
451
452 public String getClassName() {
453 return "ThreadScope";
454 }
455
456 public void setLock(boolean lock) {
457 this.locked = lock;
458 }
459
460 public void put(String name, Scriptable start, Object value) {
461
462
463 if (this.locked && !has(name, start) && !(value instanceof NativeJavaClass)) {
464
465 throw new WrappedException (new JavaScriptException("Implicit declaration of global variable '" + name +
466 "' forbidden. Please ensure all variables are explicitely declared with the 'var' keyword"));
467 }
468 this.useSession = true;
469 super.put(name, start, value);
470 }
471
472 public void put(int index, Scriptable start, Object value) {
473
474 if (this.locked && !has(index, start)) {
475 throw new WrappedException(new JavaScriptException("Global scope locked. Cannot set value for index " + index));
476 }
477 this.useSession = true;
478 super.put(index, start, value);
479 }
480
481
482 void onExec() {
483 this.useSession = false;
484 super.put(LAST_EXEC_TIME, this, new Long(System.currentTimeMillis()));
485 }
486
487 /*** Override importClass to allow reloading of classes */
488 public static void importClass(Context ctx,
489 Scriptable thisObj,
490 Object[] args,
491 Function funObj) {
492 for (int i = 0; i < args.length; i++) {
493 Object clazz = args[i];
494 if (!(clazz instanceof NativeJavaClass)) {
495 throw Context.reportRuntimeError("Not a Java class: " +
496 Context.toString(clazz));
497 }
498 String s = ((NativeJavaClass) clazz).getClassObject().getName();
499 String n = s.substring(s.lastIndexOf('.') + 1);
500 thisObj.put(n, thisObj, clazz);
501 }
502 }
503
504 public void setupPackages(ClassLoader cl) throws Exception {
505 final String JAVA_PACKAGE = "JavaPackage";
506 if (classLoader != cl) {
507 classLoader = cl;
508 Scriptable newPackages = new NativeJavaPackage("", cl);
509 newPackages.setParentScope(this);
510 newPackages.setPrototype(ScriptableObject.getClassPrototype(this, JAVA_PACKAGE));
511 super.put("Packages", this, newPackages);
512 for (int i = 0; i < BUILTIN_PACKAGES.length; i++) {
513 String pkgName = BUILTIN_PACKAGES[i];
514 Scriptable pkg = new NativeJavaPackage(pkgName, cl);
515 pkg.setParentScope(this);
516 pkg.setPrototype(ScriptableObject.getClassPrototype(this, JAVA_PACKAGE));
517 super.put(pkgName, this, pkg);
518 }
519 }
520 }
521
522 public ClassLoader getClassLoader() {
523 return classLoader;
524 }
525 }
526
527 private ThreadScope createThreadScope() throws Exception {
528 return new ThreadScope(scope, flowVars, wrapFactory);
529 }
530
531 /***
532 * Returns a new Scriptable object to be used as the global scope
533 * when running the JavaScript scripts in the context of a request.
534 *
535 * <p>If you want to maintain the state of global variables across
536 * multiple invocations of <code><map:call
537 * function="..."></code>, you need to instanciate the session
538 * object which is a property of the flow object
539 * <code>var session = flow.session</code>. This will place the
540 * newly create Scriptable object in the user's session, where it
541 * will be retrieved from at the next invocation of {@link #callFunction}.</p>
542 *
543 * @exception Exception if an error occurs
544 */
545 private void setupContext(WebContext webctx, Context context,
546 ThreadScope thrScope)
547 throws Exception {
548
549
550
551
552
553
554
555
556
557
558 FOM_Flow flow = (FOM_Flow) thrScope.get("flow", thrScope);
559 long lastExecTime = ((Long) thrScope.get(LAST_EXEC_TIME,
560 thrScope)).longValue();
561 boolean needsRefresh = false;
562 if (reloadScripts) {
563 long now = System.currentTimeMillis();
564 if (now >= lastTimeCheck + checkTime) {
565 needsRefresh = true;
566 }
567 lastTimeCheck = now;
568 }
569
570
571
572 ClassLoader contextClassloader = Thread.currentThread().getContextClassLoader();
573 thrScope.setupPackages(contextClassloader);
574 flow.pushCallContext(this, webctx, getLogger(), null);
575
576
577 synchronized (compiledScripts) {
578 List execList = new ArrayList();
579
580
581
582
583 if (lastExecTime == 0 || needsRefresh || needResolve.size() > 0) {
584 topLevelScripts.addAll(needResolve);
585 if (lastExecTime != 0 && !needsRefresh) {
586 execList.addAll(needResolve);
587 } else {
588 execList.addAll(topLevelScripts);
589 }
590 needResolve.clear();
591 }
592
593
594 for (int i = 0, size = execList.size(); i < size; i++) {
595 String sourceURI = (String)execList.get(i);
596 ScriptSourceEntry entry =
597 (ScriptSourceEntry)compiledScripts.get(sourceURI);
598 if (entry == null) {
599 Source src = this.sourceresolver.resolveURI(sourceURI);
600 entry = new ScriptSourceEntry(src);
601 compiledScripts.put(sourceURI, entry);
602 }
603
604 entry.getScript(context, this.scope, needsRefresh, this);
605 }
606
607 for (int i = 0, size = execList.size(); i < size; i++) {
608 String sourceURI = (String) execList.get(i);
609 ScriptSourceEntry entry =
610 (ScriptSourceEntry) compiledScripts.get(sourceURI);
611 long lastMod = entry.getSource().getLastModified();
612 Script script = entry.getScript(context, this.scope, false, this);
613 if (lastExecTime == 0 || lastMod > lastExecTime) {
614 script.exec(context, thrScope);
615 thrScope.onExec();
616 }
617 }
618 }
619 }
620
621 /***
622 * Compile filename as JavaScript code
623 *
624 * @param cx Rhino context
625 * @param fileName resource uri
626 * @return compiled script
627 */
628 Script compileScript(Context cx, String fileName) throws Exception {
629 Source src = this.sourceresolver.resolveURI(fileName);
630 if (src != null) {
631 synchronized (compiledScripts) {
632 ScriptSourceEntry entry =
633 (ScriptSourceEntry)compiledScripts.get(src.getURI());
634 Script compiledScript = null;
635 if (entry == null) {
636 compiledScripts.put(src.getURI(),
637 entry = new ScriptSourceEntry(src));
638 } else {
639 this.sourceresolver.release(src);
640 }
641 compiledScript = entry.getScript(cx, this.scope, false, this);
642 return compiledScript;
643 }
644 }
645 throw new FlowException(fileName + ": not found");
646
647 }
648
649 protected Script compileScript(Context cx, Scriptable scope, Source src)
650 throws Exception {
651 PushbackInputStream is = new PushbackInputStream(src.getInputStream(), ENCODING_BUF_SIZE);
652 try {
653 String encoding = findEncoding(is);
654 Reader reader = encoding == null ? new InputStreamReader(is) : new InputStreamReader(is, encoding);
655 reader = new BufferedReader(reader);
656 Script compiledScript = cx.compileReader(scope, reader,
657 src.getURI(), 1, null);
658 return compiledScript;
659 } finally {
660 is.close();
661 }
662 }
663
664
665
666 private final static int ENCODING_BUF_SIZE = 100;
667
668 Pattern encodingRE = Pattern.compile("^.*encoding//s*=//s*([^//s]*)");
669
670 /***
671 * Find the encoding of the stream, or null if not specified
672 */
673 String findEncoding(PushbackInputStream is) throws IOException {
674
675 byte[] buffer = new byte[ENCODING_BUF_SIZE];
676 int len = is.read(buffer, 0, buffer.length);
677
678 is.unread(buffer, 0, len);
679
680
681 String str = new String(buffer, 0, len, "ASCII");
682 Matcher re = encodingRE.matcher(str);
683 if (re.matches()) {
684 return re.group(1);
685 }
686 return null;
687 }
688
689 /***
690 * Calls a JavaScript function, passing <code>params</code> as its
691 * arguments. In addition to this, it makes available the parameters
692 * through the <code>flow.parameters</code> JavaScript array
693 * (indexed by the parameter names).
694 *
695 * @param funName a <code>String</code> value
696 * @param params a <code>List</code> value
697 * @param redirector
698 * @exception Exception if an error occurs
699 */
700 public Object callFunction(String funName, List params, WebContext webctx)
701 throws Exception {
702 Context context = Context.enter();
703 context.setOptimizationLevel(OPTIMIZATION_LEVEL);
704 context.setGeneratingDebug(true);
705 context.setCompileFunctionsWithDynamicScope(true);
706 context.setErrorReporter(new JSErrorReporter(getLogger()));
707 if (wrapFactory != null) {
708 context.setWrapFactory(wrapFactory);
709 }
710
711 LocationTrackingDebugger locationTracker = new LocationTrackingDebugger();
712 if (!enableDebugger) {
713
714 context.setDebugger(locationTracker, null);
715 }
716 Object ret = null;
717 ThreadScope thrScope = getSessionScope(webctx);
718 synchronized (thrScope) {
719 ClassLoader savedClassLoader =
720 Thread.currentThread().getContextClassLoader();
721 FOM_Flow flow = null;
722 try {
723 try {
724 setupContext(webctx, context, thrScope);
725 flow = (FOM_Flow) thrScope.get("flow", thrScope);
726
727
728
729
730 if (enableDebugger) {
731 if (!getDebugger().isVisible()) {
732
733 getDebugger().setVisible(true);
734 }
735 }
736
737 int size = (params != null ? params.size() : 0);
738 Scriptable parameters = context.newObject(thrScope);
739 for (int i = 0; i < size; i++) {
740 Interpreter.Argument arg = (Interpreter.Argument)params.get(i);
741 if (arg.name == null) {
742 arg.name = "";
743 }
744 parameters.put(arg.name, parameters, arg.value);
745 }
746 flow.setParameters(parameters);
747
748 Object fun = ScriptableObject.getProperty(thrScope, funName);
749 if (fun == Scriptable.NOT_FOUND) {
750 throw new FlowException("Function \"javascript:" + funName + "()\" not found");
751 }
752
753 thrScope.setLock(true);
754 ret = ScriptRuntime.call(context, fun, thrScope, new Object[0], thrScope);
755 } catch (JavaScriptException ex) {
756 throw locationTracker.getException("Error calling flowscript function " + funName, ex);
757 } catch (EcmaError ee) {
758 throw locationTracker.getException("Error calling function " + funName, ee);
759 } catch (WrappedException ee) {
760 throw locationTracker.getException("Error calling function " + funName, ee);
761 }
762 } finally {
763 thrScope.setLock(false);
764 setSessionScope(thrScope, webctx);
765 if (flow != null) {
766 flow.popCallContext();
767 }
768 Context.exit();
769 Thread.currentThread().setContextClassLoader(savedClassLoader);
770 }
771 }
772 return ret;
773 }
774
775 public void handleContinuation(String id, List params,
776 WebContext webctx) throws Exception
777 {
778 WebContinuation wk = continuationsMgr.lookupWebContinuation(id, getInterpreterID(), webctx);
779
780 if (wk == null) {
781
782
783
784
785 throw new InvalidContinuationException("The continuation ID " + id + " is invalid.");
786 }
787
788 Context context = Context.enter();
789 context.setOptimizationLevel(OPTIMIZATION_LEVEL);
790 context.setGeneratingDebug(true);
791 context.setCompileFunctionsWithDynamicScope(true);
792 LocationTrackingDebugger locationTracker = new LocationTrackingDebugger();
793 if (wrapFactory != null) {
794 context.setWrapFactory(wrapFactory);
795 }
796 if (!enableDebugger) {
797
798 context.setDebugger(locationTracker, null);
799 }
800
801
802
803
804 Continuation k = (Continuation)wk.getContinuation();
805 ThreadScope kScope = (ThreadScope)k.getParentScope();
806 synchronized (kScope) {
807 ClassLoader savedClassLoader =
808 Thread.currentThread().getContextClassLoader();
809 FOM_Flow flow = null;
810 try {
811 Thread.currentThread().setContextClassLoader(kScope.getClassLoader());
812 flow = (FOM_Flow)kScope.get("flow", kScope);
813 kScope.setLock(true);
814 flow.pushCallContext(this, webctx, getLogger(), wk);
815
816
817
818
819 if (enableDebugger) {
820 getDebugger().setVisible(true);
821 }
822 Scriptable parameters = context.newObject(kScope);
823 int size = params != null ? params.size() : 0;
824 for (int i = 0; i < size; i++) {
825 Interpreter.Argument arg = (Interpreter.Argument)params.get(i);
826 parameters.put(arg.name, parameters, arg.value);
827 }
828 flow.setParameters(parameters);
829 FOM_WebContinuation fom_wk = new FOM_WebContinuation(wk);
830 fom_wk.setParentScope(kScope);
831 fom_wk.setPrototype(ScriptableObject.getClassPrototype(kScope,
832 fom_wk.getClassName()));
833 Object[] args = new Object[] {k, fom_wk};
834 try {
835 ScriptableObject.callMethod(flow,
836 "handleContinuation", args);
837 } catch (JavaScriptException ex) {
838 throw locationTracker.getException("Error calling continuation", ex);
839
840 } catch (EcmaError ee) {
841 throw locationTracker.getException("Error calling continuation", ee);
842
843 }
844 } finally {
845 kScope.setLock(false);
846 setSessionScope(kScope, webctx);
847 if (flow != null) {
848 flow.popCallContext();
849 }
850 Context.exit();
851 Thread.currentThread().setContextClassLoader(savedClassLoader);
852 }
853 }
854 }
855
856 private Throwable unwrap(JavaScriptException e) {
857 Object value = e.getValue();
858 while (value instanceof Wrapper) {
859 value = ((Wrapper)value).unwrap();
860 }
861 if (value instanceof Throwable) {
862 return (Throwable)value;
863 }
864 return e;
865 }
866 }