Helma Logo
main list history
previous version  overview  next version

Version 18 by hannes on 05. February 2008, 17:23

=== Rhino Continuations

Rhino continuations allow to capture the state of a complete running script to be resumed later on.


Rhino continuations only work in interpreter mode with the compiler disabled, so make sure that you have the following in your app.properties:

  rhino.optlevel = -1

While adding continuation and callback support to existing Helma releases has been a bit of a hack, recent Helma snapshots have added features to make that easier, namely the introduction of *req.uri|https://dev.helma.org/trac/helma/changeset/8774* and *req.actionProcessor|https://dev.helma.org/trac/helma/changeset/8775*.

The following script is a first prototype for adding Continuation support to the Helma 1.7 web framework.

<% this.attachments %>

This adds four static methods to the Continuation constructor:

* Continuation.nextId() - returns the ID for the next contiuation for cases where it isn't possible to use Continuation.nextUrl() (namely GET requests, as Continuation.nextUrl())and the continuation_id has to be set via hidden input field (GET requests).
* Continuation.nextUrl() - returns the URL to the next continuation of the currently running action to be used in HTML POST forms and links. Note that the continuation doesn't exist yet until Continuation.nextPage() is called.
* Continuation.nextPage() - suspends execution and stores the suspended action in the user's session.
* Continuation.resume() - must be called in the onRequest() method of the current object in order to allow the continuation to be resumed.

Here's an example action that makes use of this framework:

  function test_continuation_action() {
    res.write(<form method="post" action={Continuation.nextUrl()}>
                <input name="foo"/>
                <input type="submit"/>
    var foo = req.data.foo;
    res.write('<a href="' + Continuation.nextUrl() + '">click here</a>');
    res.write("you said: " + foo);

This action displays three pages: the first renders a form, the second a link, and the third will show a message containing the string entered in the first page.

When running the example, you will notice that the continuations have the same URI path as the original action, just with a <code>continuation_id</code> query parameter added:


This has the great benefit that trying to run a non-existing continuation (either because the link has been passed outside the originating session, or because the session has expired, or because the client does not support cookies) will actually result in the right action being invoked - it will simply result in the action to be started from the beginning. So in order to detect all these cases, it is sufficient to test for a <code>continuation_id</code> parameter at the very beginning of the action:

  function continuation_action() {
    if (req.data.continuation_id) {
      // Request has a continuation_id, but we landed at the beginning
      // beginning of the action. Either the session timed out,
      // or the link comes from another session.
      res.debug("Continuation invalid or expired. Starting over.");
    // Continue rendering first page
    // From here on, req.data.continuation_id is expected to be set

An area that still needs work is serialization support, as continuations currently will not work in conjunction with persistent sessions. Persistent HopObjects shouldn't be included in a serialized continuation, so we need to convert Nodes into NodeHandles in the output stream, and convert back to Nodes in the input stream. The latter currently fails in session restore because it requires a Transactor thread. For global references we need to make sure they're excluded from serialization. Helma 1.7 seems like a good timeframe to fix these issues.

=== Jetty Continuations

Jetty continuations are a mechanism to keep HTTP connections pending without having a Java thread associated with the request.


This is not a "real" continuation as this isn't supported by Java. Instead, it relies on "that all actions taken by a filter or servlet before a call to suspend(long) are either idempotent (can be retried) or are made conditional on isPending() so they are not performed on retried requests".


Jetty continuations are a means to allow AJAX polling without a thread penalty.