r9752 - in helma-ng/trunk: apps/demo apps/demo/skins modules/helma/webapp
[email protected] Thu, 14 May 2009 15:18:33 +0200 (CEST)
| Newsgroups | gmane.comp.java.helma.cvs |
|---|---|
| Message-ID | <20090514131833.359493D0D6@mia> |
Author: hannes
Date: 2009-05-14 15:18:33 +0200 (Thu, 14 May 2009)
New Revision: 9752
Modified:
helma-ng/trunk/apps/demo/actions.js
helma-ng/trunk/apps/demo/config.js
helma-ng/trunk/apps/demo/skins/continuation.txt
helma-ng/trunk/modules/helma/webapp/continuation.js
Log:
Rewrite helma/webapp/continuation not to use JS continuations at all, storing just the data in the http session instead, making the code much easier to read and run.
Details at http://dev.helma.org/trac/helma/changeset/9752
Modified: helma-ng/trunk/apps/demo/actions.js
===================================================================
--- helma-ng/trunk/apps/demo/actions.js 2009-05-14 13:18:26 UTC (rev 9751)
+++ helma-ng/trunk/apps/demo/actions.js 2009-05-14 13:18:33 UTC (rev 9752)
@@ -49,47 +49,54 @@
}
// demo for continuation support
-function continuation(req) {
+function continuation(req, run) {
- // local data - this is the data that is shared between continuations of this function
- var data = {};
- var session = new ContinuationSession("welcome", "ask_name", "ask_food", "ask_animal", "result");
- req = session.start();
+ if (!run) {
+ return SkinnedResponse('skins/continuation.txt', {
+ page: "welcome",
+ title: "Continuations"
+ });
+ }
- // render intro page
- req = session.step(1).render(SkinnedResponse('skins/continuation.txt', {
- session: session,
- title: "Continuations",
- data: data
- }));
-
- req = session.step(2).render(SkinnedResponse('skins/continuation.txt', {
- session: session,
- title: "Question 1",
- data: data
- }));
- if (req.isPost)
- data.name = req.params.name;
+ var session = new ContinuationSession(req);
- req = session.step(3).render(SkinnedResponse('skins/continuation.txt', {
- session: session,
- title: "Question 2",
- data: data
- }));
- if (req.isPost)
- data.food = req.params.food;
+ session.addPage("ask_name", function(req) {
+ return SkinnedResponse('skins/continuation.txt', {
+ session: session,
+ page: session.page,
+ title: "Question 1"
+ })
+ });
- req = session.step(4).render(SkinnedResponse('skins/continuation.txt', {
- session: session,
- title: "Question 3",
- data: data
- }));
- if (req.isPost)
- data.animal = req.params.animal;
+ session.addPage("ask_food", function(req) {
+ if (req.isPost)
+ session.data.name = req.params.name;
+ return SkinnedResponse('skins/continuation.txt', {
+ session: session,
+ page: session.page,
+ title: "Question 2"
+ });
+ });
- session.step(5).render(SkinnedResponse('skins/continuation.txt', {
- session: session,
- title: "Thank you!",
- data: data
- }));
+ session.addPage("ask_animal", function(req) {
+ if (req.isPost)
+ session.data.food = req.params.food;
+ return SkinnedResponse('skins/continuation.txt', {
+ session: session,
+ page: session.page,
+ title: "Question 3"
+ });
+ });
+
+ session.addPage("result", function(req) {
+ if (req.isPost)
+ session.data.animal = req.params.animal;
+ return SkinnedResponse('skins/continuation.txt', {
+ session: session,
+ page: session.page,
+ title: "Thank you!"
+ });
+ });
+
+ return session.run();
}
Modified: helma-ng/trunk/apps/demo/config.js
===================================================================
--- helma-ng/trunk/apps/demo/config.js 2009-05-14 13:18:26 UTC (rev 9751)
+++ helma-ng/trunk/apps/demo/config.js 2009-05-14 13:18:33 UTC (rev 9752)
@@ -9,7 +9,6 @@
];
exports.middleware = [
- 'helma/webapp/continuation',
'helma/logging',
];
Modified: helma-ng/trunk/apps/demo/skins/continuation.txt
===================================================================
--- helma-ng/trunk/apps/demo/skins/continuation.txt 2009-05-14 13:18:26 UTC (rev 9751)
+++ helma-ng/trunk/apps/demo/skins/continuation.txt 2009-05-14 13:18:33 UTC (rev 9752)
@@ -1,72 +1,73 @@
<% extends 'base.html' %>
<% subskin 'content' -------------------------------------------------------------------- %>
-<% render <% session.page %> %>
+<% render <% page %> %>
-<% subskin 'welcome' %>
+<% subskin 'welcome' | markdown %>
- <p>This is a little game to show off continuations support in Helma NG.
- All you'll have to do is answer a few easy questions.</p>
+ This is a little game to show off continuations support in Helma NG.
+ All you'll have to do is answer a few easy questions.</p>
- <p><a href="<% session.forward %>">Click here to start.</a></p>
+ [Click here to start](/continuation/run)
- <p>What's special about this demo is that all pages are generated by
- <a href="http://dev.helma.org/trac/helma/browser/helma-ng/trunk/apps/demo/actions.js#L44">one
- single JavaScript function</a> which is run only once, but resumed and suspended
- earch time a page is rendered. Local variables are stored inside the
- function when it is supsended, so no extra effort is required to
- manage state between pages.</p>
+ What's special about this demo is that all pages are generated by
+ [one single JavaScript function][1] providing multiple action callbacks.
+ Local variables are stored in the HTTP user session, so no extra effort is required to
+ manage state between pages.</p>
- <% render 'list' %>
+ [1]: http://dev.helma.org/trac/helma/browser/helma-ng/trunk/apps/demo/actions.js#L44
-<% subskin 'ask_name' %>
+<% subskin 'ask_name' | markdown %>
- What is your name? <form method="post" action="<% session.forward %>">
- <input name="name" value="<% data.name %>">
- <input type="submit">
- </form>
+ What is your name?
+ <form method="post" action="<% session.forward %>">
+ <input name="name" value="<% session.data.name %>">
+ <input type="submit">
+ </form>
- <p><a href="<% session.back %>">back</a> <a href="<% session.forward %>">forward</a></p>
+ [forward](<% session.forward %>)
- <% render 'list' %>
+ <% render 'list' %>
-<% subskin 'ask_food' %>
+<% subskin 'ask_food' | markdown %>
- What is your favorite food? <form method="post" action="<% session.forward %>">
- <input name="food" value="<% data.food %>">
- <input type="submit">
- </form>
+ What is your favorite food?
+ <form method="post" action="<% session.forward %>">
+ <input name="food" value="<% session.data.food %>">
+ <input type="submit">
+ </form>
- <p><a href="<% session.back %>">back</a> <a href="<% session.forward %>">forward</a></p>
+ [back](<% session.back %>) [forward](<% session.forward %>)
- <% render 'list' %>
+ <% render 'list' %>
-<% subskin 'ask_animal' %>
+<% subskin 'ask_animal' | markdown %>
- What is your favorite animal? <form method="post" action="<% session.forward %>">
- <input name="animal" value="<% data.animal %>">
- <input type="submit">
- </form>
+ What is your favorite animal?
+ <form method="post" action="<% session.forward %>">
+ <input name="animal" value="<% session.data.animal %>">
+ <input type="submit">
+ </form>
- <p><a href="<% session.back %>">back</a> <a href="<% session.forward %>">forward</a></p>
+ [back](<% session.back %>) [forward](<% session.forward %>)
- <% render 'list' %>
+ <% render 'list' %>
-<% subskin 'result' %>
+<% subskin 'result' | markdown %>
- Thanks for your collaboration. Your name is <b><% data.name %></b>,
- you love to eat <b><% data.food %></b> and your favorite animal
- is <b><% data.animal %></b>.
+ Thanks for your collaboration. Your name is <b><% session.data.name %></b>,
+ you love to eat <b><% session.data.food %></b> and your favorite animal
+ is <b><% session.data.animal %></b>.
- <p><a href="<% session.back %>">back</a></p>
+ [back](<% session.back %>)
- <% render 'list' %>
+ <% render 'list' %>
<% subskin 'list'%>
- <% for value in <% data %> render 'item'
+ <% for value in <% session.data %> render 'item'
| wrap "<div style='color: gray;'>" "</div>"
- %>
+ %>
<% subskin 'item' %>
<div><% index %>: <% value %></div>
Modified: helma-ng/trunk/modules/helma/webapp/continuation.js
===================================================================
--- helma-ng/trunk/modules/helma/webapp/continuation.js 2009-05-14 13:18:26 UTC (rev 9751)
+++ helma-ng/trunk/modules/helma/webapp/continuation.js 2009-05-14 13:18:33 UTC (rev 9752)
@@ -2,132 +2,83 @@
* Continuation support for Helma NG
*/
-export('ContinuationSession', 'handleRequest');
+export('ContinuationSession');
var system = require('helma/system');
var log = require('helma/logging').getLogger(__name__);
+importClass(java.util.HashMap);
-var __shared__ = true;
+function ContinuationSession(req) {
-function ContinuationSession() {
+ var id = req.params._cid;
+
+ if (!id) {
+ throw {redirect: getContinuationUrl(0)};
+ }
+
+ var data = getData(req) || new HashMap();
var pages = [];
- var ids = [];
- var currentstep = 0;
- for (var i = 0; i < arguments.length; i++) {
- pages.push(arguments[i]);
+ var callbacks = [];
+ var length = 0;
+ var step = parseInt(req.params._cstep) || 0;
+
+ this.addPage = function(name, callback) {
+ pages.push(name);
+ callbacks.push(callback);
+ length = pages.length;
}
- var length = pages.length;
- this.start = function() {
- var req = require('helma/webapp/env').req;
- if (!req.params.helma_continuation) {
- // set query param so helma knows to switch rhino optimization level to -1
- throw { redirect: getContinuationUrl(0) };
+ this.run = function() {
+ var callback = callbacks[step];
+ if (!(typeof callback === "function")) {
+ throw new Error("invalid continuation step: " + step);
}
- if (system.getOptimizationLevel() > -1) {
- system.setOptimizationLevel(-1);
- throw { retry: true };
- }
- currentstep = 0;
- ids[0] = id = req.params.helma_continuation;
- var continuation = createContinuation();
- if (continuation instanceof Continuation) {
- log.debug("Recording continuation start: " + id);
- setCallback(req, id, continuation);
- return req;
- } else {
- return continuation;
- }
+ var result = callback(req);
+ setData(req, id, data);
+ return result;
};
- this.step = function(step) {
- currentstep = step - 1;
- return this;
- };
+ Object.defineProperty(this, "data", {
+ value: new ScriptableMap(data)
+ });
- this.render = function(res) {
- var req = require('helma/webapp/env').req;
- // capture continuation and store it in callback container
- var id = getContinuationId(currentstep + 1);
- var continuation = createContinuation();
- if (continuation instanceof Continuation) {
- if (!getCallback(req, id))
- setCallback(req, id, continuation);
- // Exit current js context by calling empty continuation with return value
- new org.mozilla.javascript.NativeContinuation()(res);
- } else {
- return continuation;
- }
- };
+ Object.defineProperty(this, "page", {
+ get: function() pages[step]
+ });
- this.page = function() {
- return pages[currentstep];
- };
+ Object.defineProperty(this, "step", {
+ get: function() step,
+ set: function(s) { step = s; }
+ })
this.back = function() {
- return currentstep > 0 ? getContinuationUrl(currentstep - 1) : null;
+ return step > 0 ? getContinuationUrl(step - 1) : null;
};
this.forward = function() {
- return currentstep < length ? getContinuationUrl(currentstep + 1) : null;
+ return step < length ? getContinuationUrl(step + 1) : null;
};
function getContinuationUrl(step) {
- var req = require('helma/webapp/env').req;
- return req.path + "?helma_continuation=" + getContinuationId(step);
+ id = id || generateId();
+ return req.path + "?_cid=" + id + "&_cstep=" + String(step);
}
- function getContinuationId(step) {
- if (ids[step] == null) {
- ids[step] = generateId();
- }
- return ids[step];
- }
-
- function createContinuation() {
- return new Continuation();
- }
-
function generateId() {
return Math.ceil(Math.random() * Math.pow(2, 32)).toString(36);
}
}
-/**
- * Continuation middleware function
- * @param req the request
- * @return the continuation result
- */
-function handleRequest(req) {
- var id = req.params.helma_continuation;
- var continuation = getCallback(req, id);
- if (continuation) {
- if (system.getOptimizationLevel() > -1) {
- system.setOptimizationLevel(-1);
- throw { retry: true };
- }
- log.debug("resuming continuation " + id + " with req " + req);
- return continuation(req);
- }
- return req.process();
-}
-
-var setCallback = function(req, id, func) {
- if (!req.session.data.continuation) {
- req.session.data.continuation = {};
- }
- log.debug("Registered continuation: " + id);
- req.session.data.continuation[id] = func;
- // fauxsessions[id] = func;
+var setData = function(req, id, data) {
+ log.debug("Setting continuation data: " + id);
+ req.session.data[id] = data;
};
-var getCallback = function(req, id) {
- if (!req.session.data.continuation || !req.session.data.continuation[id]) {
+var getData = function(req, id) {
+ id = id || req.params._cid;
+ if (!id) {
return null;
}
- return req.session.data.continuation[id];
- // return fauxsessions[id];
+ return req.session.data[id];
};
-// awful hack to make this work on google app engine
-// var fauxsessions = {}