001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.flow.javascript.fom;
018:
019: import java.beans.IntrospectionException;
020: import java.beans.Introspector;
021: import java.beans.PropertyDescriptor;
022: import java.io.OutputStream;
023: import java.util.ArrayList;
024: import java.util.Arrays;
025: import java.util.Collections;
026: import java.util.Enumeration;
027: import java.util.HashMap;
028: import java.util.HashSet;
029: import java.util.List;
030: import java.util.Map;
031: import java.util.Set;
032:
033: import org.apache.avalon.framework.CascadingRuntimeException;
034: import org.apache.avalon.framework.component.WrapperComponentManager;
035: import org.apache.avalon.framework.context.Context;
036: import org.apache.avalon.framework.context.ContextException;
037: import org.apache.avalon.framework.logger.Logger;
038: import org.apache.avalon.framework.service.ServiceManager;
039: import org.apache.cocoon.components.ContextHelper;
040: import org.apache.cocoon.components.LifecycleHelper;
041: import org.apache.cocoon.components.flow.ContinuationsManager;
042: import org.apache.cocoon.components.flow.WebContinuation;
043: import org.apache.cocoon.components.flow.Interpreter.Argument;
044: import org.apache.cocoon.environment.ObjectModelHelper;
045: import org.apache.cocoon.environment.Redirector;
046: import org.apache.cocoon.environment.Request;
047: import org.apache.cocoon.environment.Response;
048: import org.apache.cocoon.environment.Session;
049: import org.apache.cocoon.util.ClassUtils;
050: import org.mozilla.javascript.JavaScriptException;
051: import org.mozilla.javascript.NativeJavaClass;
052: import org.mozilla.javascript.NativeJavaObject;
053: import org.mozilla.javascript.Script;
054: import org.mozilla.javascript.Scriptable;
055: import org.mozilla.javascript.ScriptableObject;
056: import org.mozilla.javascript.Undefined;
057: import org.mozilla.javascript.Wrapper;
058: import org.mozilla.javascript.continuations.Continuation;
059:
060: /**
061: * Implementation of FOM (Flow Object Model).
062: *
063: * @since 2.1
064: * @author <a href="mailto:coliver.at.apache.org">Christopher Oliver</a>
065: * @author <a href="mailto:reinhard.at.apache.org">Reinhard P\u00F6tz</a>
066: * @version CVS $Id: FOM_Cocoon.java 518747 2007-03-15 20:30:03Z antonio $
067: */
068: public class FOM_Cocoon extends ScriptableObject {
069:
070: class CallContext {
071: CallContext caller;
072: Context avalonContext;
073: ServiceManager serviceManager;
074: FOM_JavaScriptInterpreter interpreter;
075: Redirector redirector;
076: Logger logger;
077: Scriptable request;
078: Scriptable response;
079: Scriptable session;
080: Scriptable context;
081: Scriptable parameters;
082: Scriptable log;
083: WebContinuation lastContinuation;
084: FOM_WebContinuation fwk;
085: PageLocalScopeImpl currentPageLocal;
086:
087: public CallContext(CallContext caller,
088: FOM_JavaScriptInterpreter interp,
089: Redirector redirector, ServiceManager manager,
090: Context avalonContext, Logger logger,
091: WebContinuation lastContinuation) {
092: this .caller = caller;
093: this .interpreter = interp;
094: this .redirector = redirector;
095: this .serviceManager = manager;
096: this .avalonContext = avalonContext;
097: this .logger = logger;
098: this .lastContinuation = lastContinuation;
099: if (lastContinuation != null) {
100: fwk = new FOM_WebContinuation(lastContinuation);
101: Scriptable scope = FOM_Cocoon.this .getParentScope();
102: fwk.setParentScope(scope);
103: fwk.setPrototype(getClassPrototype(scope, fwk
104: .getClassName()));
105: this .currentPageLocal = fwk.getPageLocal();
106: }
107: if (this .currentPageLocal != null) {
108: // "clone" the page local scope
109: this .currentPageLocal = this .currentPageLocal
110: .duplicate();
111: } else {
112: this .currentPageLocal = new PageLocalScopeImpl(
113: getTopLevelScope(FOM_Cocoon.this ));
114: }
115: pageLocal.setDelegate(this .currentPageLocal);
116: }
117:
118: public FOM_WebContinuation getLastContinuation() {
119: return fwk;
120: }
121:
122: public void setLastContinuation(FOM_WebContinuation fwk) {
123: this .fwk = fwk;
124: if (fwk != null) {
125: pageLocal.setDelegate(fwk.getPageLocal());
126: this .lastContinuation = fwk.getWebContinuation();
127: } else {
128: this .lastContinuation = null;
129: }
130: }
131:
132: public Scriptable getSession() {
133: if (session != null) {
134: return session;
135: }
136: Map objectModel = ContextHelper
137: .getObjectModel(this .avalonContext);
138: session = new FOM_Session(getParentScope(),
139: ObjectModelHelper.getRequest(objectModel)
140: .getSession(true));
141: return session;
142: }
143:
144: public Scriptable getRequest() {
145: if (request != null) {
146: return request;
147: }
148: Map objectModel = ContextHelper
149: .getObjectModel(this .avalonContext);
150: request = new FOM_Request(getParentScope(),
151: ObjectModelHelper.getRequest(objectModel));
152: return request;
153: }
154:
155: public Scriptable getContext() {
156: if (context != null) {
157: return context;
158: }
159: Map objectModel = ContextHelper
160: .getObjectModel(this .avalonContext);
161: context = new FOM_Context(getParentScope(),
162: ObjectModelHelper.getContext(objectModel));
163: return context;
164: }
165:
166: public Scriptable getResponse() {
167: if (response != null) {
168: return response;
169: }
170: Map objectModel = ContextHelper
171: .getObjectModel(this .avalonContext);
172: response = org.mozilla.javascript.Context.toObject(
173: ObjectModelHelper.getResponse(objectModel),
174: getParentScope());
175: return response;
176: }
177:
178: public Scriptable getLog() {
179: if (log != null) {
180: return log;
181: }
182: log = org.mozilla.javascript.Context.toObject(logger,
183: getParentScope());
184: return log;
185: }
186:
187: public Scriptable getParameters() {
188: return parameters;
189: }
190:
191: public void setParameters(Scriptable parameters) {
192: this .parameters = parameters;
193: }
194: }
195:
196: private CallContext currentCall;
197: protected PageLocalScopeHolder pageLocal;
198:
199: public String getClassName() {
200: return "FOM_Cocoon";
201: }
202:
203: // Called by FOM_JavaScriptInterpreter
204: static void init(Scriptable scope) throws Exception {
205: //FIXME(SW) what is the exact purpose of defineClass() ??
206: defineClass(scope, FOM_Cocoon.class);
207: // defineClass(scope, FOM_Request.class);
208: // defineClass(scope, FOM_Response.class);
209: // defineClass(scope, FOM_Cookie.class);
210: // defineClass(scope, FOM_Session.class);
211: // defineClass(scope, FOM_Context.class);
212: // defineClass(scope, FOM_Log.class);
213: defineClass(scope, FOM_WebContinuation.class);
214: defineClass(scope, PageLocalImpl.class);
215: }
216:
217: public void pushCallContext(FOM_JavaScriptInterpreter interp,
218: Redirector redirector, ServiceManager manager,
219: Context avalonContext, Logger logger,
220: WebContinuation lastContinuation) {
221: if (pageLocal == null) {
222: pageLocal = new PageLocalScopeHolder(getTopLevelScope(this ));
223: }
224:
225: // The call context will use the current sitemap's service manager when looking up components
226: ServiceManager sitemapManager;
227: try {
228: sitemapManager = (ServiceManager) avalonContext
229: .get(ContextHelper.CONTEXT_SITEMAP_SERVICE_MANAGER);
230: } catch (ContextException e) {
231: throw new CascadingRuntimeException(
232: "Cannot get sitemap service manager", e);
233: }
234:
235: this .currentCall = new CallContext(currentCall, interp,
236: redirector, sitemapManager, avalonContext, logger,
237: lastContinuation);
238: }
239:
240: public void popCallContext() {
241: // Clear the scope attribute
242: FOM_JavaScriptFlowHelper.setFOM_FlowScope(
243: this .getObjectModel(), null);
244:
245: this .currentCall = this .currentCall.caller;
246: // reset current page locals
247: if (this .currentCall != null) {
248: pageLocal.setDelegate(this .currentCall.currentPageLocal);
249: } else {
250: pageLocal.setDelegate(null);
251: }
252: }
253:
254: public FOM_WebContinuation jsGet_continuation() {
255: // FIXME: This method can return invalid continuation! Is it OK to do so?
256: return currentCall.getLastContinuation();
257: }
258:
259: public void jsSet_continuation(Object obj) {
260: FOM_WebContinuation fwk = (FOM_WebContinuation) unwrap(obj);
261: currentCall.setLastContinuation(fwk);
262: }
263:
264: public FOM_WebContinuation jsFunction_sendPage(String uri,
265: Object obj, Object wk) throws Exception {
266: FOM_WebContinuation fom_wk = (FOM_WebContinuation) unwrap(wk);
267: if (fom_wk != null) {
268: // save page locals
269: fom_wk.setPageLocal(pageLocal.getDelegate());
270: }
271: forwardTo(uri, unwrap(obj), fom_wk);
272: return fom_wk;
273: }
274:
275: public Scriptable jsFunction_createPageLocal() {
276: return pageLocal.createPageLocal();
277: }
278:
279: public void jsFunction_processPipelineTo(String uri, Object map,
280: Object outputStream) throws Exception {
281: Object out = unwrap(outputStream);
282: if (!(out instanceof OutputStream)) {
283: throw new JavaScriptException(
284: "expected a java.io.OutputStream instead of " + out);
285: }
286: getInterpreter().process(getParentScope(), this , uri, map,
287: (OutputStream) out);
288: }
289:
290: public void jsFunction_redirectTo(String uri, boolean isGlobal)
291: throws Exception {
292: if (isGlobal) {
293: this .currentCall.redirector.globalRedirect(false, uri);
294: } else {
295: this .currentCall.redirector.redirect(false, uri);
296: }
297: }
298:
299: public void jsFunction_sendStatus(int sc) {
300: this .currentCall.redirector.sendStatus(sc);
301: }
302:
303: /*
304:
305: NOTE (SM): These are the hooks to the future FOM Event Model that will be
306: designed in the future. It has been postponed because we think
307: there are more important things to do at the moment, but these
308: are left here to indicate that they are planned.
309:
310: public void jsFunction_addEventListener(String eventName,
311: Object function) {
312: // what is this?
313: }
314:
315: public void jsFunction_removeEventListener(String eventName,
316: Object function) {
317: // what is this?
318: }
319:
320: */
321:
322: /**
323: * Access components.
324: *
325: * TODO: Do we want to restrict the access of sitemap components? (RP)
326: * TODO: Do we want to raise an error or return null? (RP)
327: */
328: public Object jsFunction_getComponent(String id) throws Exception {
329: // To use rhino1.5r4-continuations-R26.jar as a workaround for COCOON-1579: Uncomment the next line and comment the original return.
330: // return getServiceManager().lookup(id);
331: return org.mozilla.javascript.Context.javaToJS(
332: getServiceManager().lookup(id), getParentScope());
333: }
334:
335: /**
336: * Release pooled components.
337: *
338: * @param component a component
339: */
340: public void jsFunction_releaseComponent(Object component)
341: throws Exception {
342: this .getServiceManager().release(unwrap(component));
343: }
344:
345: /**
346: * Load the script file specified as argument.
347: *
348: * @param filename a <code>String</code> value
349: * @return an <code>Object</code> value
350: * @exception JavaScriptException if an error occurs
351: */
352: public Object jsFunction_load(String filename) throws Exception {
353: org.mozilla.javascript.Context cx = org.mozilla.javascript.Context
354: .getCurrentContext();
355: Scriptable scope = getParentScope();
356: Script script = getInterpreter().compileScript(cx, filename);
357: return script.exec(cx, scope);
358: }
359:
360: /**
361: * Setup an object so that it can access the information provided to regular components.
362: * This is done by calling the various Avalon lifecycle interfaces implemented by the object, which
363: * are <code>LogEnabled</code>, <code>Contextualizable</code>, <code>Serviceable</code>,
364: * <code>Composable</code> (even if deprecated) and <code>Initializable</code>.
365: * <p>
366: * <code>Contextualizable</code> is of primary importance as it gives access to the whole object model
367: * (request, response, etc.) through the {@link org.apache.cocoon.components.ContextHelper} class.
368: * <p>
369: * Note that <code>Configurable</code> is ignored, as no configuration exists in a flowscript that
370: * can be passed to the object.
371: *
372: * @param obj the object to setup
373: * @return the same object (convenience that allows to write <code>var foo = cocoon.setupObject(new Foo());</code>).
374: * @throws Exception if something goes wrong during setup.
375: */
376: public Object jsFunction_setupObject(Object obj) throws Exception {
377: LifecycleHelper.setupComponent(unwrap(obj), this .getLogger(),
378: this .getAvalonContext(), this .getServiceManager(),
379: new WrapperComponentManager(this .getServiceManager()),
380: null,// roleManager
381: null,// configuration
382: true);
383: return obj;
384: }
385:
386: /**
387: * Create and setup an object so that it can access the information provided to regular components.
388: * This is done by calling the various Avalon lifecycle interfaces implemented by the object, which
389: * are <code>LogEnabled</code>, <code>Contextualizable</code>, <code>Serviceable</code>,
390: * <code>Composable</code> (even if deprecated) and <code>Initializable</code>.
391: * <p>
392: * <code>Contextualizable</code> is of primary importance as it gives access to the whole object model
393: * (request, response, etc.) through the {@link org.apache.cocoon.components.ContextHelper} class.
394: * <p>
395: * Note that <code>Configurable</code> is ignored, as no configuration exists in a flowscript that
396: * can be passed to the object.
397: *
398: * @param classObj the class to instantiate, either as a String or a Rhino NativeJavaClass object
399: * @return an set up instance of <code>clazz</code>
400: * @throws Exception if something goes wrong either during instantiation or setup.
401: */
402: public Object jsFunction_createObject(Object classObj)
403: throws Exception {
404: Object result;
405:
406: if (classObj instanceof String) {
407: result = ClassUtils.newInstance((String) classObj);
408:
409: } else if (classObj instanceof NativeJavaClass) {
410: Class clazz = ((NativeJavaClass) classObj).getClassObject();
411: result = clazz.newInstance();
412:
413: } else {
414: throw new IllegalArgumentException(
415: "cocoon.createObject expects either a String or Class argument, but got "
416: + classObj.getClass());
417: }
418:
419: return jsFunction_setupObject(result);
420: }
421:
422: /**
423: * Dispose an object that has been created using {@link #jsFunction_createObject(Object)}.
424: *
425: * @param obj
426: * @throws Exception
427: */
428: public void jsFunction_disposeObject(Object obj) throws Exception {
429: LifecycleHelper.decommission(obj);
430: }
431:
432: /**
433: * Base JS wrapper for Cocoon's request/session/context objects.
434: * <p>
435: * FIXME(SW): The only thing added to the regular Java object is the fact that
436: * attributes can be accessed as properties. Do we want to keep this?
437: */
438: private static abstract class AttributeHolderJavaObject extends
439: NativeJavaObject {
440:
441: private static Map classProps = new HashMap();
442: private Set propNames;
443:
444: public AttributeHolderJavaObject(Scriptable scope,
445: Object object, Class clazz) {
446: super (scope, object, clazz);
447: this .propNames = getProperties(object.getClass());
448: }
449:
450: /** Compute the names of JavaBean properties so that we can filter them our in get() */
451: private static Set getProperties(Class clazz) {
452: Set result = (Set) classProps.get(clazz);
453: if (result == null) {
454: try {
455: PropertyDescriptor[] descriptors = Introspector
456: .getBeanInfo(clazz)
457: .getPropertyDescriptors();
458: result = new HashSet();
459: for (int i = 0; i < descriptors.length; i++) {
460: result.add(descriptors[i].getName());
461: }
462: } catch (IntrospectionException e) {
463: // Cannot introspect: just consider there are no properties
464: result = Collections.EMPTY_SET;
465: }
466: classProps.put(clazz, result);
467: }
468: return result;
469: }
470:
471: protected abstract Enumeration getAttributeNames();
472:
473: protected abstract Object getAttribute(String name);
474:
475: public Object[] getIds() {
476: // Get class Ids
477: Object[] classIds = super .getIds();
478:
479: // and add attribute names
480: ArrayList idList = new ArrayList(Arrays.asList(classIds));
481: Enumeration iter = getAttributeNames();
482: while (iter.hasMoreElements()) {
483: idList.add(iter.nextElement());
484: }
485: return idList.toArray();
486: }
487:
488: public boolean has(String name, Scriptable start) {
489: return super .has(name, start) || getAttribute(name) != null;
490: }
491:
492: public Object get(String name, Scriptable start) {
493: Object result;
494: // Filter out JavaBean properties. We only want methods of the underlying object.
495: if (this .propNames.contains(name)) {
496: result = NOT_FOUND;
497: } else {
498: result = super .get(name, start);
499: }
500: if (result == NOT_FOUND) {
501: result = getAttribute(name);
502: if (result != null) {
503: result = wrap(start, result, null);
504: } else {
505: result = NOT_FOUND;
506: }
507: }
508: return result;
509: }
510: }
511:
512: /**
513: * JS wrapper for Cocoon's request object.
514: * <p>
515: * Request <em>parameters</em> are also present as properties on this object.
516: * Note that this is different from <code>FOM_Context</code> and <code>FOM_Session</code>
517: * that do the same with <em>attributes</em>.
518: */
519: public static class FOM_Request extends AttributeHolderJavaObject {
520: private final Request request;
521:
522: public FOM_Request(Scriptable scope, Request request) {
523: super (scope, request, Request.class);
524: this .request = request;
525: }
526:
527: protected Enumeration getAttributeNames() {
528: return this .request.getParameterNames();
529: }
530:
531: protected Object getAttribute(String name) {
532: return this .request.getParameter(name);
533: }
534: }
535:
536: /**
537: * JS wrapper for Cocoon's session object.
538: * <p>
539: * Session attributes are also present as properties on this object.
540: */
541: public static class FOM_Session extends AttributeHolderJavaObject {
542: private final Session session;
543:
544: public FOM_Session(Scriptable scope, Session session) {
545: super (scope, session, Session.class);
546: this .session = session;
547: }
548:
549: protected Enumeration getAttributeNames() {
550: return this .session.getAttributeNames();
551: }
552:
553: protected Object getAttribute(String name) {
554: return this .session.getAttribute(name);
555: }
556: }
557:
558: /**
559: * JS wrapper for Cocoon's context object.
560: * <p>
561: * Context attributes are also present as properties on this object.
562: */
563: public static class FOM_Context extends AttributeHolderJavaObject {
564: private final org.apache.cocoon.environment.Context context;
565:
566: public FOM_Context(Scriptable scope,
567: org.apache.cocoon.environment.Context context) {
568: super (scope, context,
569: org.apache.cocoon.environment.Context.class);
570: this .context = context;
571: }
572:
573: protected Enumeration getAttributeNames() {
574: return this .context.getAttributeNames();
575: }
576:
577: protected Object getAttribute(String name) {
578: return this .context.getAttribute(name);
579: }
580: }
581:
582: public Scriptable jsGet_request() {
583: return currentCall.getRequest();
584: }
585:
586: public Scriptable jsGet_response() {
587: return currentCall.getResponse();
588: }
589:
590: public Scriptable jsGet_log() {
591: return currentCall.getLog();
592: }
593:
594: public Scriptable jsGet_context() {
595: return currentCall.getContext();
596: }
597:
598: public Scriptable jsGet_session() {
599: return currentCall.getSession();
600: }
601:
602: /**
603: * Get Sitemap parameters
604: *
605: * @return a <code>Scriptable</code> value whose properties represent
606: * the Sitemap parameters from <map:call>
607: */
608: public Scriptable jsGet_parameters() {
609: return getParameters();
610: }
611:
612: public Scriptable getParameters() {
613: return currentCall.getParameters();
614: }
615:
616: void setParameters(Scriptable value) {
617: currentCall.setParameters(value);
618: }
619:
620: // unwrap Wrapper's and convert undefined to null
621: private static Object unwrap(Object obj) {
622: if (obj instanceof Wrapper) {
623: obj = ((Wrapper) obj).unwrap();
624: } else if (obj == Undefined.instance) {
625: obj = null;
626: }
627: return obj;
628: }
629:
630: // Make everything available to JavaScript objects implemented in Java:
631:
632: /**
633: * Get the current request
634: * @return The request
635: */
636: public Request getRequest() {
637: return ObjectModelHelper.getRequest(ContextHelper
638: .getObjectModel(currentCall.avalonContext));
639: }
640:
641: /**
642: * Get the current session
643: * @return The session (may be null)
644: */
645: public Session getSession() {
646: return ObjectModelHelper
647: .getRequest(
648: ContextHelper
649: .getObjectModel(currentCall.avalonContext))
650: .getSession(true);
651: }
652:
653: /**
654: * Get the current response
655: * @return The response
656: */
657: public Response getResponse() {
658: return ObjectModelHelper.getResponse(ContextHelper
659: .getObjectModel(currentCall.avalonContext));
660: }
661:
662: /**
663: * Get the current context
664: * @return The context
665: */
666: public org.apache.cocoon.environment.Context getContext() {
667: return ObjectModelHelper.getContext(ContextHelper
668: .getObjectModel(currentCall.avalonContext));
669: }
670:
671: /**
672: * Get the current object model
673: * @return The object model
674: */
675: public Map getObjectModel() {
676: return ContextHelper.getObjectModel(currentCall.avalonContext);
677: }
678:
679: private Context getAvalonContext() {
680: return currentCall.avalonContext;
681: }
682:
683: private Logger getLogger() {
684: return currentCall.logger;
685: }
686:
687: public ServiceManager getServiceManager() {
688: return currentCall.serviceManager;
689: }
690:
691: private FOM_JavaScriptInterpreter getInterpreter() {
692: return currentCall.interpreter;
693: }
694:
695: /**
696: * Required by FOM_WebContinuation. This way we do not make whole Interpreter public
697: * @return interpreter Id associated with this FOM.
698: */
699: public String getInterpreterId() {
700: return getInterpreter().getInterpreterID();
701: }
702:
703: /**
704: * Call the Cocoon Sitemap to process a page
705: * @param uri Uri to match
706: * @param bean Input to page
707: * @param fom_wk Current Web continuation (may be null)
708: */
709:
710: public void forwardTo(String uri, Object bean,
711: FOM_WebContinuation fom_wk) throws Exception {
712: getInterpreter().forwardTo(getTopLevelScope(this ), this , uri,
713: bean, fom_wk, this .currentCall.redirector);
714: }
715:
716: /**
717: * Perform the behavior of <map:call continuation="blah">
718: * This can be used in cases where the continuation id is not encoded
719: * in the request in a form convenient to access in the sitemap.
720: * Your script can extract the id from the request and then call
721: * this method to process it as normal.
722: * @param kontId The continuation id
723: * @param parameters Any parameters you want to pass to the continuation (may be null)
724: */
725: public void handleContinuation(String kontId, Scriptable parameters)
726: throws Exception {
727: List list = null;
728: if (parameters == null || parameters == Undefined.instance) {
729: parameters = getParameters();
730: }
731: Object[] ids = parameters.getIds();
732: list = new ArrayList();
733: for (int i = 0; i < ids.length; i++) {
734: String name = ids[i].toString();
735: Argument arg = new Argument(name,
736: org.mozilla.javascript.Context
737: .toString(getProperty(parameters, name)));
738: list.add(arg);
739: }
740: getInterpreter().handleContinuation(kontId, list,
741: this .currentCall.redirector);
742: }
743:
744: /**
745: * Return this continuation if it is valid, or first valid parent
746: */
747: private FOM_WebContinuation findValidParent(FOM_WebContinuation wk) {
748: if (wk != null) {
749: WebContinuation wc = wk.getWebContinuation();
750: while (wc != null && wc.disposed()) {
751: wc = wc.getParentContinuation();
752: }
753: if (wc != null) {
754: return new FOM_WebContinuation(wc);
755: }
756: }
757:
758: return null;
759: }
760:
761: /**
762: * Create a Bookmark WebContinuation from a JS Continuation with the last
763: * continuation of sendPageAndWait as its parent.
764: * PageLocal variables will be shared with the continuation of
765: * the next call to sendPageAndWait().
766: * @param k The JS continuation
767: * @param ttl Lifetime for this continuation (zero means no limit)
768: */
769: public FOM_WebContinuation jsFunction_makeWebContinuation(Object k,
770: Object ttl) throws Exception {
771: double d = org.mozilla.javascript.Context.toNumber(ttl);
772: FOM_WebContinuation result = makeWebContinuation(
773: (Continuation) unwrap(k),
774: findValidParent(jsGet_continuation()), (int) d);
775: result.setPageLocal(pageLocal.getDelegate());
776: currentCall.setLastContinuation(result);
777: return result;
778: }
779:
780: /**
781: * Create a Web Continuation from a JS Continuation
782: * @param k The JS continuation (may be null - null will be returned in that case)
783: * @param parent The parent of this continuation (may be null)
784: * @param timeToLive Lifetime for this continuation (zero means no limit)
785: */
786: public FOM_WebContinuation makeWebContinuation(Continuation k,
787: FOM_WebContinuation parent, int timeToLive)
788: throws Exception {
789: if (k == null) {
790: return null;
791: }
792: WebContinuation wk;
793: ContinuationsManager contMgr;
794: contMgr = (ContinuationsManager) getServiceManager().lookup(
795: ContinuationsManager.ROLE);
796: wk = contMgr.createWebContinuation(unwrap(k),
797: (parent == null ? null : parent.getWebContinuation()),
798: timeToLive, getInterpreter().getInterpreterID(), null);
799: FOM_WebContinuation result = new FOM_WebContinuation(wk);
800: result.setParentScope(getParentScope());
801: result.setPrototype(getClassPrototype(getParentScope(), result
802: .getClassName()));
803: return result;
804: }
805: }
|