Updated: 11/5/2005; 6:05:06 PM.
Chris Double's Radio Weblog
        

Monday, September 08, 2003

I work on other stuff besides the web based continuation framework. Most of my time is taken up on other interesting things but I'm unable to write about it due to NDA constraints. Pity!
11:26:22 PM      

Another thing I added to the framework was the ability to expire continuations if a logical transaction was completed. For example, if a series of data was being entered then committed to the database, at that point of the commit I wanted all previous continuations to be stale so the user couldn't backtrack via the back button or bookmark functionality of the browser. I did this with a 'with-transaction' form:

(define (data-entry)
  (with-transaction
   (let ((data (new-data)))
     (let ((request (show (mk-data-page1 data))))
       (set-record-field data "name" (hashtable/get request 'name)))
     (let ((request (show (mk-data-page2 data))))
       (set-record-field data "address1" (hashtable/get request 'address1))
       (set-record-field data "address2" (hashtable/get request 'address2))
       (set-record-field data "address3" (hashtable/get request 'address3)))
     (let ((request (show (mk-data-page3 data))))
       (set-record-field data "more_data" (hashtable/get request 'more_data)))
     (hashtable/put! *data* (get-record-field data "id") data)))
  (data-entry))

In this example, while within the 'with-transaction' form you can use the back button or bookmark. But as soon as the 'with-transaction' blocks is exited all continuations created within the block are replaced with a continuation that goes to a 'Transaction expired' page with a 'continue' button. The continue button takes them to the end of the with-transaction block which in this case just calls 'data-entry' again, so you start from the beginning.

The 'with-transaction' block can be nested and only continuations created within that nesting are expired. So far this approach seems to prevent 'double submit' problems.


11:22:56 PM      

Avi Bryant has a great post discussing ways of handling state in continuation based web frameworks. It is a difficult problem and at the moment I'm backtracking all state. Unfortunately the method mentioned of serialising sessions holding points to all continuations in the session didn't work with SISC Scheme. It appears to still hold seperate copies of the state in each continuation. I plan to dig into this further when I get some time. I plan to do the 'Snapshot any object' approach.

Avi is also correct in that the block-href function was inspired by Seaside functionality. I originally added a 'call-page' method that enabled calling a 'subroutine' page and returning back to the calling page. I then went through the Seaside tutorial seeing if I could do similar things to Seaside and discovered that 'call-page' was really 'block-href'. Instead of calling 'show' within 'call-page' I just execute the block. Block is really the wrong name for a Lisp framework though. I'm terrible at naming things.

I struck a problem recently whereby I couldn't resume a continuation stored from the persistant store from another browser session if that continuation relied on 'POST' or 'GET' data. Given my previous example:

(define (data-entry)
  (let ((data (new-data)))
    (show (mk-data-page1 data))
    ;; (A)
    (set-record-field data "name" (get-param "name"))
    (show (mk-data-page2 data))
    ;; (B)
    (set-record-field data "address1" (get-param "address1"))
    (set-record-field data "address2" (get-param "address2"))
    (set-record-field data "address3" (get-param "address3"))
    (show (mk-data-page3 data))
    ;; (C)
    (set-record-field data "more_data" (get-param "more_data"))
    (hashtable/put! *data* (get-record-field data "id") data)
    (data-entry)))

The continuation resumed at point (B) will work if resumed from the current web server and browser session. But if I bookmark it and resume it later it fails as the 'get-param' call could no longer access the POST/GET data from the HTTP request object. I changed the code so that 'show' returns a hashtable of the POST/GET fields. Something like:

(define (data-entry)
  (let ((data (new-data)))
    (let ((request (show (mk-data-page1 data))))
      (set-record-field data "name" (hashtable/get request 'name)))
    (let ((request (show (mk-data-page2 data))))
      (set-record-field data "address1" (hashtable/get request 'address1))
      (set-record-field data "address2" (hashtable/get request 'address2))
      (set-record-field data "address3" (hashtable/get request 'address3)))
    (let ((request (show (mk-data-page3 data))))
      (set-record-field data "more_data" (hashtable/get request 'more_data)))
    (hashtable/put! *data* (get-record-field data "id") data)
    (data-entry)))


11:15:22 PM      

Bill Clementson writes more about continuation based web frameworks. He points to a SISC maze servlet written using this style.
11:00:35 PM      

© Copyright 2005 Chris Double.
 
September 2003
Sun Mon Tue Wed Thu Fri Sat
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        
Aug   Oct



Click here to visit the Radio UserLand website.

Listed on BlogShares

Click to see the XML version of this web page.

Click here to send an email to the editor of this weblog.