Use continuations to develop complex Web applications

Discussions

News: Use continuations to develop complex Web applications

  1. The article shows the continuations used inside Cocoon to easily develop Web application.

    If you've ever developed a non-trivial Web application, you know that development complexity is increased by the fact that Web browsers allow users to follow arbitrary navigation paths through the application. No matter where the user navigates, the onus is on you, the developer, to keep track of the possible interactions and ensure that your application works correctly. While the traditional MVC approach does allow you to handle these cases, there are other options available to help resolve application complexity. Developer and frequent developerWorks contributor Abhijit Belapurkar walks you through a continuations-based alternative that could simplify your Web application development efforts.

    Use continuations to develop complex Web applications

    Threaded Messages (27)

  2. The article's a little long, so here's a summary for people familiar with Struts.

    The author suggests using a continuation id, which is a composition of a session id and an Action identifier. What you can do one with one, you can do with the other. Under breathless headings such as You make the rules! the author uses continuations to solve navigational problems in the exact same way that I have addressed them with Struts (which is hardly MVC nirvana).

    OK, there is one twist. A single user can have multiple concurrent continuation ids. The author gives an example of a user, who is doing form-based data entry, branching off new browser windows midway through the process, and continuing data entry in both windows. The author claims that this solves the problem where "one transaction overrides the other", but this doesn't appear to be true if the user is editing the same underlying data in both windows.

    The author also boasts that continuations will allow you to consolidate all of your Action methods into "a single monolithic program". He follows through on his threats by presenting a piece of Java code at the end... that is in desperate need of refactoring into individual Action methods.

    I still haven't figured out if continuations bring something new and useful to the table, or if they are the latest fashion for Weak Typing Fetishists. I don't think this article provides a very compelling use case.
  3. the peripheral device 'user'[ Go to top ]

    If you look at the piece of javascript in the article you get a very quick sense for the use of continuations in webapplications. Perspective is flipped from the user/client to the server. The steps that are normally distributed over several struts actions are here in logical sequence. Since it is a single method they share their local variables, no need to store them and look them up each time. It seems a natural representation to me.

    I guess a single continuation would be appropriate for defining a single unit of work (business transaction) since that is inherently somewhat akin to a function, or JVM frame. Of course a real-world application would not be build from a single continuation just as a procedural application is not build in one big procedure. So then we'd get a call-stack of continuations.
    So this does not necessarily lead to a monolithic application.

    Very interesting. Anybody build any applications like this? Perhaps using Rife?

    I guess this could be implemented without continuations but with blocking threads. That would not scale of course...
  4. the peripheral device 'user'[ Go to top ]

    Hi Joost,

    using RIFE we built several applications that use continuations to perform 'islands of functionality' (unit of work). One of those is publically available as Bamboo (http://bamboo.dev.java.net). Typical locations where continuations made things easier are:
    * the setup wizard
    * the creation of polls in multiple steps
    * moderation features that allow you to select and move messages easily with a simple wizard

    All these steps are really multi-step actions that you only want to go forward after everything has been done. Sure you can do this in several actions and pass data around or keep it manually in the session (and remember to invalidate it, etc etc), but with continuations all this is totally natural and requires no additional planning.
  5. I still haven't figured out if continuations bring something new and useful to the table, or if they are the latest fashion for Weak Typing Fetishists. I don't think this article provides a very compelling use case.

    The magic of continuations allows you to write the controller in linear fashion, with the Flow layer's sendPage method taking care of:

        * Sending the specified page to the web browser client
        * Saving the state of the Flow script
        * Getting input when the client response comes in (might be a few minutes later)
        * Transparently restarting the Flow script as if the user had instantly submitted the page.

    More related links:
    1- Ovidiu presentation in Ghent GetTogether 2002: http://outerthought.net/gettogether/handouts/10%20-%20presentation%20-%20ovidiu.pdf

    2-Model-View-Controller in Cocoon using continuations-based control flow (by Ovidiu):
    http://www.webweavertech.com/ovidiu/weblog/archives/000042.html

    3-Official cocoon flow docs: http://cocoon.apache.org/2.1/userdocs/flow/index.html
    4-Cocoon wiki: http://wiki.apache.org/cocoon/
  6. The article's a little long, so here's a summary for people familiar with Struts...

    The above is a very poor summary of an excellent article.
    I still haven't figured out if continuations bring something new and useful to the table, or if they are the latest fashion for Weak Typing Fetishists. I don't think this article provides a very compelling use case.

    The idea of using continuations to implement Web applications isn't new. It originated in Scheme with Christian Quiennec (see the links to his papers in the article).

    And continuations are not related to weak typing.

    And for what it's worth somebody ported the Cocoon flow engine to Struts: http://struts.sourceforge.net/struts-flow
  7. Under breathless headings such as You make the rules! the author uses continuations to solve navigational problems in the exact same way that I have addressed them with Struts (which is hardly MVC nirvana).

    How Cocoon uses continuations is akin to an Ada rendezvous. Even though a UML activity diagram can show rendezvous, there's an inherent lack of modularity to continuations that make them ill-suited for automations such as reflection, XDoclet, and MDA. Continuations encourage monolithic routines, a bad thing.
  8. How Cocoon uses continuations is akin to an Ada rendezvous. Even though a UML activity diagram can show rendezvous, there's an inherent lack of modularity to continuations that make them ill-suited for automations such as reflection, XDoclet, and MDA.
    Continuations encourage monolithic routines, a bad thing.

    "monolithic routines", wow! Why you said that? Is not every routine "monolithic after all?

    Or do you think is better to have the controller spreaded in a lot of files?
  9. "...a continuation is a saved snapshot of the executable state of a program at any given point in time..."

    The main problem or show killer I can see in using this cocoon version of continuation is that "the snapshot of the executable state of a program" covers ONLY the state/snapshot of the Javascript program.

    "The Rhino implementation has very good integration with the Java platform. It is possible to access and reuse any Java class or object that exists in the application..."

    Unfortunately if any state is saved in the Java VM (say in a static variable), the continuation won't recover the snapshot of the state when it resumes.

    To me, the title "Continuations in Java code" is therefore misleading in that the Rhino implementation does not support a Java (program state) continuation but only Javascript (program state) continuation .

    I wish I was mistaken, but at least this is what I found from my last experiement with the Rhino suport of Continuation.

    Please correct me otherwise.
  10. It is possible to access and reuse any Java class or object that exists in the application..."Unfortunately if any state is saved in the Java VM (say in a static variable), the continuation won't recover the snapshot of the state when it resumes.

    Can you explain why a webapp would be storing state information in a static variable? Especially since continuations let you access your objects across multiple requests, as you point out. The state should be saved in them.

    Also, as the author of the article mentioned, there is a Java implementation of flow in Cocoon. Javascript is not required.
  11. Can you explain why a webapp would be storing state information in a static variable

    Static variable is not a very good example. A better one is the need to save states in the http session (assuming running in a J2EE servlet container.) My understanding is that the continuation won't be able to recover the session snapshot (as it is outside the Rhino universe.)

    >Also, as the author of the article mentioned, there is a Java implementation of flow in Cocoon. Javascript is not required.

    This is the misleading part I tried to point out in my original message. Although the Javascript part is physically written in Java, it doesn't mean continuation will work at the Java level. It only works at the logical Javascript level (implemented physically in Java.) The above http session states is one such example.

    An experiment to see the problem:
    1)Use the Java implementation of flow in Cocoon;
    2)save states in a http session via (1); and
    3)execute a continuation and see what happened to those states in the http session;
  12. My understanding is that the continuation won't be able to recover the session snapshot (as it is outside the Rhino universe.) Although the Javascript part is physically written in Java, it doesn't mean continuation will work at the Java level. It only works at the logical Javascript level (implemented physically in Java.) The above http session states is one such example.An experiment to see the problem:1)Use the Java implementation of flow in Cocoon;2)save states in a http session via (1); and3)execute a continuation and see what happened to those states in the http session;

    I cannot even imagine how this could be true, unless the servlet container is broken. You are saying that somehow using a continuation modifies the behavior of how the servlet container manages session objects?

    Furthermore, saving stuff in the session is quite unnecessary are class member variables are automatically stored in the session.
  13. My understanding is that the continuation won't be able to recover the session snapshot (as it is outside the Rhino universe.)

    Cocoon provides a set of system objects for use by Flowscripts. We call this set of objects the Flow Object Model (FOM). The Flow Object Model consists of the following objects:

        * Cocoon
        * Request
        * Response
        * Session
        * Context
        * Cookie
        * Log
        * WebContinuation

    More info:
    http://cocoon.apache.org/2.1/userdocs/flow/api.html
  14. Cocoon provides a set of system objects for use by Flowscripts. We call this set of objects the Flow Object Model (FOM). The Flow Object Model consists of ...

    It would be really nice if there is a Factory class/method that takes an input root object, and returns a deep "FOM" clone (recusively and resolving all circular references) so that the clone can be used and captured via continuation. Apparently there are things that don't make sense to be captured like a file handle, but even if such deep FOM clone functionality is avaiable just for JavaBean will be very useful.

    This way, the very powerful continuation can be used in context other than a web envrionment. eg. Capture the phase 2 of a 2-phase commit transaction via a continuation.
  15. It would be really nice if there is a Factory class/method that takes an input root object, and returns a deep "FOM" clone (recusively and resolving all circular references) so that the clone can be used and captured via continuation.

    By the "input root object" I mean any object that is not already part of the FOM. And the returned clone becomes part of the FOM.
  16. You can reach the same target using diferent techniques.

    The problem of a deep FOM clone is that you can easily full the memory. i.e: suppose you are using an O/R mapping tool to access a big database.

    The cocoon community is also looking forward to improve continuations. Some of the interesting things are stackless continuations.
  17. You're correct that only the call stack of the JavaScript program is preserved in the continuation, however Java object references held in local variables are still accessible when the program is resumed.

    Also there is a mostly equivalent pure Java version that actually does capture the JVM call stack (by inserting additional bytecode into each method when classes are loaded).
  18. ..there is a mostly equivalent pure Java version that actually does capture the JVM call stack (by inserting additional bytecode into each method when classes are loaded).

    Is this "version" the Cocoon Java implementation ? If not, where can it be foudn ?
  19. ..there is a mostly equivalent pure Java version that actually does capture the JVM call stack (by inserting additional bytecode into each method when classes are loaded).
    Is this "version" the Cocoon Java implementation ? If not, where can it be foudn ?

    Try cocoon 2.1.6 --> http://cocoon.apache.org/

    In the main SVN trunk version (2.2-dev) javaflow was enhanced to recompile the java sources on the fly when the file was changed. The same efect as we currently have with javascript.
  20. Java continuations?[ Go to top ]

    Also there is a mostly equivalent pure Java version that actually does capture the JVM call stack (by inserting additional bytecode into each method when classes are loaded).
    That's interesting. Can you provide a link or a name for this java implementation?

    groeten,
    Joost
  21. Java continuations?[ Go to top ]

    Ah, sorry. I did not RTFA entirely.
    Some googling turned up this link to Brian McAllister's weblog. Apparently some people want to start a JSR for JVM support for this. In J2SE 7.0 in 2008 perhaps? :-)
  22. thats a lot of extra work[ Go to top ]

    passing an extra object to every method? spare me.
    this seems like the perfect thing to use AOP for.
    give advice on EVERY method! woohoo
  23. thats a lot of extra work[ Go to top ]

    passing an extra object to every method? spare me.this seems like the perfect thing to use AOP for.give advice on EVERY method! woohoo

    Yep. There is new work to be done, but in exchange to a lot of work that you don't need to do again. ;-)
  24. thats a lot of extra work[ Go to top ]

    passing an extra object to every method? spare me.this seems like the perfect thing to use AOP for.give advice on EVERY method! woohoo

    If you read more carefully you'll see that you do _not_ need to pass an extra object to every method in Cocoon or any of the other continuation based systems the author referred to.

    The author was referring to CPS (Continuation Passing Style) which is an approach to conceptualizing (and compiling) programs.

    Cocoon makes the "current" continuation of a function available as a JavaScript function object reference:

    function foo() {
       var cont = arguments.continuation;
       cont(100); // same effect as return 100;
       return 100; // not reached
    }

    Although the above may still look strange to you, no AOP is required.
  25. An interactive Web application consists of a collection of scripts wherein a single interaction comprises one script delivering a page to the browser (then ending), the user completing and submitting the form at some later point in time, and another (possibly different) script handling the submitted form.
    Is that supposed to be bad? On the opposite, this is the perfect separation of concerns.
    Depending on the size of the state machine and the amount of data required to maintain a client's current state (as a Web application may be accessed by a large number of clients at any given time), the application logic could become unnecessarily cluttered and complex.
    How something can be continuous if there are so many choices? What if input data is invalid? What if a user decided to cancel an operation or to visit a different page?
    The client may choose to hit the back button on the browser at any time during the sequence of state transitions, or may clone the browser window to initiate a parallel sequence of actions. Either move could lead to multiple (sometimes even concurrent) submissions corresponding to states that had already been passed in the original interaction. As a result, the application would be forced to keep track of every individual transaction and provide the correct response to each of them.
    If pages are served as "no-cache"/"no-store", then users will not see stale page when going back in session history. Parallel actions -- they should be possible for different objects. For the same object each of cloned windows should simply display the latest state, it is quite simple.
    A similar problem could arise in the case where a Web application was trying to gather information from the user in a series of forms spread over multiple pages. If the generation of a later form depended on the combination of responses the user had provided in the previous ones, then the application would be forced to keep track of the responses entered as part of each interaction and make sure that the correct page was returned in response to each one of them.
    Pages should not depend on each other, they should depend on model state. Again, I do not see anything wrong in having state for pages which comprise a wizard.
    Because it is important to be able to restart the remaining part of the application logic upon receiving the user's response, a unique id is also generated to serve as the key to search for this particular continuation in the continuations repository. This id is also sent down, along with the page displayed to the user in an appropriate manner such that the form submission will cause the id to be sent back in the response.
    If UI is built around a well-designed OO model, then traditional MVC app can use object ID instead of sending continuation ID.
    From the outside, the continuations-based approach cannot be differentiated from an MVC architecture. The user is free to go back to any previously submitted Web page, change any data required, and resubmit the form via the browser. The difference is on the inside, in the amount of code-juggling required to get the navigation to work correctly. The continuations-based approach requires no additional effort for this, because a continuation id is always associated with each submitted page. The server just has to look up the correct continuation for a given submitted page and ask it to resume.
    From this I conclude that suggested approach does not even try to solve double submit/ stale page problem. So, these problems whould have to be solved separately.
    Invalidating a continuation makes it impossible to go back to the page corresponding to that continuation and change the associated form data before re-submitting the form. As a result, there is no continuation available to resume, and an error is reported.
    In traditional MVC app, after form is submitted, just delete the object corresponding to the form. Going back in the session history would cause page reload for the deleted object, and error would be shown.
    Unlike MVC implementations, the continuations-based approach provides a workaround to the code tangle that can result from trying to handle cloning. In the continuations-based approach, the user can enter different data on the original and the cloned windows, and submit both in parallel. The continuation is then resumed in two threads (basically, the server threads that have been assigned the handling of the two requests) with two sets of submitted values. This outcome is far preferable to what commonly happens in Web applications not based in continuations
    I don't see how two results for the same object may be better than one. If the objects are different, then this is a legit operation and can be easily implemented using traditional MVC.
    There are two options for sending the continuation id to the user's browser: It can be embedded as a hidden field in the form that is sent back,
    Not all pages are forms.
    or it can be embedded in the URL to which the form will be posted.
    So, navigating from external page or directly entering URL will break the continuation?
  26. Is that supposed to be bad? On the opposite, this is the perfect separation of concerns.

    The problem here is that the "concern" is the same in all the files. It is only programmer concern. This is not the perfect separation of concerns. We are still looking forward to improve it. ;-)
    How something can be continuous if there are so many choices? What if input data is invalid? What if a user decided to cancel an operation or to visit a different page?

    Very easy: As most of programming languages you can stop the procedure code in any place you want. You can use a "loop" to check invalid data or just a return statement if the user press cancel.
    If pages are served as "no-cache"/"no-store", then users will not see stale page when going back in session history. Parallel actions -- they should be possible for different objects. For the same object each of cloned windows should simply display the latest state, it is quite simple.

    Of course you can use the same techniques in continuations. The diference with continuations is that the user can go back more "safely", because the "back" continuation store the state of the program at that time. Something like a debugger that allow you to go back to the last instruction and change a variable value and continue. This is the magic against a simple "no-cache"/"no-store" HTML metatag. :-D
    Pages should not depend on each other, they should depend on model state. Again, I do not see anything wrong in having state for pages which comprise a wizard.

    The continuation is in fact a kind of "state" saved automatically. You don't need to write a single line of code! The program stops until the user send the form back.

    And pages can the "next page" can depend on the current page. ie: Suppose you are trying to buy a movie. In one page we select the gender, then based on that we show more questions related to the gender (ie: year, or whatever) or a list of movies. In this cases the future pages depends on the current page.
    If UI is built around a well-designed OO model, then traditional MVC app can use object ID instead of sending continuation ID.

    Not necesarily all the applications deal with databases. The continuation ID is a key to avoid conflicts in case we will use the same object in 2 diferent situations.
    In traditional MVC app, after form is submitted, just delete the object corresponding to the form. Going back in the session history would cause page reload for the deleted object, and error would be shown.

    Same approach here. ;-)
    Not all pages are forms.

    Yep. You don't need to use continuations on every page.
    So, navigating from external page or directly entering URL will break the continuation?

    Nope. You will go exactly to the same continuation as the URL pointed to you. And from this point you start a new history (with a new continuation ID generated).
  27. The diference with continuations is that the user can go back more "safely", because the "back" continuation store the state of the program at that time. Something like a debugger that allow you to go back to the last instruction and change a variable value and continue. This is the magic against a simple "no-cache"/"no-store" HTML metatag. :-D
    So for each step the whole app state will be stored? I find this a little excessive...
    The program stops until the user send the form back.
    If I understand it correctly, flow engine decides on what to do next based on the current continuation. Flow engine receives continuation ID from a browser, either as hidded field or as URL parameter. What if browser sends a page which is not a form and does not have continuation ID attached? Does flow engine store "current continuation" for cases when browser does not supply the ID?
  28. What if browser sends a page which is not a form and does not have continuation ID attached?

    It is considered a diferent request. Maybe a new one. Clearly, it is no part of the continuation process or perhaps start a new one.
    Does flow engine store "current continuation" for cases when browser does not supply the ID?

    The continuations have a timeout.