Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Secrets of lightweight development success, Part 9: Continuations-based frameworks

Improving on the Java Web development experience

Bruce Tate (bruce.tate@j2life.com), President, RapidRed
Bruce Tate
Bruce Tate is a father, mountain biker, and kayaker in Austin, Texas. He's the author of three best-selling Java books, including the Jolt winner Better, Faster, Lighter Java. He recently released Spring: A Developer's Notebook. He spent 13 years at IBM and is now the founder of the J2Life, LLC, consultancy, where he specializes in lightweight development strategies and architectures based on Java technology and Ruby.

Summary:  By letting you look at Web requests as a single application instead of multiple requests, continuations can make it easier to manage state, improve the way components fit together, and simplify vexing problems, such as the Back button and threading. This article explores the continuations server approach.

View more content in this series

Date:  29 Nov 2005
Level:  Introductory

Activity:  6463 views
Comments:  

Riding over the ledge for the third time, I swerved around the rock, my front wheel slipped, I gripped the brake, and flipped wildly over the handlebars yet again. I was frustrated, battered, and bloodied. It had been the third time this week that I crashed on the cluttered, twisting, dropping ledge. I needed to think about the problem in a different way. Eventually, I caught sight of a more experienced biker going over the ledge with more speed. He didn't even try to avoid the rocks. He just sprang up to lighten his bike and let the wheels go over what I had been going around. I felt like a fool for making it all so hard.

Web programming in the Java™ programming language is kind of like that. We make things harder than they have to be. You need to keep things stateless to make them scale better. You code many independent responses to user requests in the form of servlets or JavaServer Pages (JSPs). You're left with many independent request/response blocks forming no coherent whole. Each one needs to determine where it is in the context of the whole application. You have to swerve around each tiny rock in your path, remembering state with each request. Assume too much, and you'll find yourself flipping over the handlebars as the user clicks the Back button, or opens a second session, or tries to go directly to an older URL. You want to assume that the user will execute tasks in the right order, but you can't. Most Java frameworks just don't work that way.

In this article, I introduce you to a radically different kind of Web server: a continuation-based Web server. Of course, the Java programming language doesn't have native support for continuations (although other programming languages do). Here, I'll examine continuation-based servers. In "Secrets of lightweight development success, Part 8," I examined one such framework called Seaside, followed by a few Java technology frameworks that provide limited continuation support through various creative means.

Continuation basics

As you learned in Part 7, a continuation object is like an object representing a frozen point in time. Call the continuation object, and you return to that point. Let's look at an example of continuations in the Ruby programming language because Ruby continuations are a little easier for most people to understand than continuations in some of the other languages. (Continuations in Smalltalk work in a similar way.) You'll see three distinct steps:

Capture a continuation
In Ruby, you use callcc, which means call with continuation. Call with continuation gives you a code block, and the continuation (or a saved call stack, with instance variables), which you assign to a variable. Think of a continuation as all of the program that hasn't executed yet.
Continue processing
The callcc statement doesn't change the state of execution beyond introducing the continuation object. After the callcc, you don't have to do anything else special.
Call the continuation
At some later time, when you call a continuation, you return to that previous point in time. Think of the callcc as replacing the current call stack with the call stack in the continuation object. In Ruby, execution resumes at the line of code immediately after the original callcc that captured the continuation.

A continuation example in Ruby

The following example shows a loop that captures a continuation when it has a value of 2:


Listing 1. A loop that captures a continuation when it has a value of 2

def loop
  cont=nil
  for i in 1..4
    puts i
    callcc {|continuation| cont=continuation} if i==2
  end
  return cont
end

Ruby code is relatively easy to read, so even if you're unfamiliar with the language, you should be able to understand what's going on with a little help. The statement def loop defines a method. The next two lines define a variable, start a for loop, and print out the current value of i. The magic happens in the fifth line. I perform a call with continuation and pass in a code block, but only if i is 2. Inside the code block, I assign the new continuation to the cont variable. Each time I call loop, it returns a continuation. Ruby captures a call stack and instance variables (in this case, the values of cont and i.) In irb, the interpreter for Ruby, I load the loop program and call it with the code:


Listing 2. Load the loop and call it with the code

prompt< c=loop
1
2
3
4

Now I can call the continuation and pick up the loop after i is set to 2 and printed:

prompt< c.call
3
4

Amazingly enough, it goes back in time, to when I captured the continuation. This interesting abstraction is very useful for certain problems.


Continuations in Web servers

Continuations are particularly useful when you want to suspend processing for some reason and later pick up where you left off. But isn't that exactly what happens with Web applications? You process a user's request, store your state, ask the user for more information, and wait. When the user calls the Web server again (it might be some minutes later), you need to go to the state dictionary, read the user's response, and then continue processing. Lather, rinse, repeat.

If you had a continuation, you could create a continuation object, store it away with a unique ID generator, and ask the user for more information. Trivial application server code would look something like this:


Listing 3. Application server code

def ask_user(webpage_id)
  id=Id_genrator.new_id
  continuation=callcc...
  continuations[id]=continuation
  load_user_webpage(id, screen_id)
end

(Note that this is not a real framework -- just some code that shows you what the main details might look like in a continuations-based application server.)

Then the Web server could work on other Web requests. When the user submitted your page and specified the ID you passed him, you could just restore the continuation and continue. The Web server code would work something like this:


Listing 4. Web server code

while(True) do
  request=get_latest_request()
  if request.is_a_new_request do
    start_new_app(request)
  else
    continuation=continuations[request.id]
    continuation.call(request)
  end
end

New requests are simple: You simply start a new Web application. Otherwise, you just load the continuation from a dictionary, using the ID that you stored when you made the user request.

Look at it as a shift in perspective. With traditional approaches, you're thinking from the browser's perspective, with many individual requests. With the continuation approach, you're looking at things as one big application, with several calls back to the user. It may be hard to get your head around what I've shown you so far, but that's OK. What's important is the usage model. A shopping cart starts to look something like this:


Listing 5. Shopping cart

def checkout(cart)
  items=cart.items
  shipping_address=get_shipping_address()
  billing_address=get_billing_address if billing_address.is_separate_address()
  credit_card_info=get_credit_card_info()
  process_checkout(items, shipping_address, billing_address, credit_card_info)
end

Though each get call goes back to the user, you don't have to save state, or restore state when control comes back to you. The continuation that gets captured by the framework takes care of that for you.


The advantages of continuations

You can see the primary benefit of continuations immediately. You don't have to preserve and load state every time you return to the user for more information. That's an incredible advantage, and if you need to build a Web site with complex flows, this kind of server can be a huge time-saver. Your code is easier to write and much easier to maintain.

With all the sophisticated Web frameworks available in Java technology, it's amazing to me that so many of them handle the Back button so poorly. With continuations, your users can navigate back not just one page but to any previous page in the browser history. You don't have to build in custom support because the Web server has the complete call stack for that point in time. The Back button works seamlessly. If you need to invalidate the Back button at any point, it's easy. You just take past continuations for the user out of the continuation dictionary. Threading, too, becomes much easier because each continuation has its own private set of data.

It's amazing that this single higher abstraction can make such a tremendous difference in your Web programming, but it really does. If the Java language had native support for continuations, we'd all be coding this way. In fact, some Java frameworks such as Apache Cocoon, RIFE, and Spring Web flow try to use this model with varying degrees of success using techniques such as state machines (in RIFE and Spring Web flow), other languages (such as the JavaScript engine in Apache Cocoon) and byte-code enhancement (in RIFE). They do improve on typical Java solutions, but they don't compare to the user experience with the best servers in other languages.


The disadvantages of continuations

Of course, there's no such thing as a free lunch. You typically see three or four arguments against using continuations:

Session state
In some ways, you're making it easy to store more data in the session by automating session management. Simply declaring an instance variable commits it to the session. When you make it easy to store session data, developers will probably store more state data, limiting scalability. Of course, that's also the price of higher-level languages.
Scalability
Saving a call stack can be expensive. Saving many of them may be prohibitive. Interestingly, if you store only the parts of the stack that are different from one user to the next, you can limit the amount of stack data that you store to something reasonable. This approach, called partial continuations, has promise.
Ugly URLs
If you store an ugly identifier with each user request, your URLs will not be beautiful or predictable. You can mitigate this problem some by exposing a pretty part of the URL, then appending an ugly identifier. This is the approach Amazon.com takes, and it seems to work well. However, this limitation is relatively serious.

There may be no such thing as a free lunch, but continuation servers seem to give you the next best thing: excellent value for your money. I believe we'll all be using continuation servers in some form in five years, or maybe even sooner. We might see one of the Java derivatives catch on, or we might see continuation servers in other languages help to make those languages more prominent.


Resources

Learn

  • Better, Faster, Lighter Java, by Bruce A. Tate and Justin Gehtland (O'Reilly, 2004), provides a good overview of lightweight development.

  • Programming Ruby, by Dave Thomas (Pragmatic Bookshelf, 2004), is the best Ruby book out there.

  • Continuations Made Simple and Illustrated (comp.lang.python newsgroup) is a good resource for information about continuations in the Python programming language.

  • Read Martin Fowler's excellent article about Closure (martinfowler.com, September 2004).

  • Seaside is an amazing framework based on the Smalltalk language and continuations.

  • Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.

Get products and technologies

Discuss

About the author

Bruce Tate

Bruce Tate is a father, mountain biker, and kayaker in Austin, Texas. He's the author of three best-selling Java books, including the Jolt winner Better, Faster, Lighter Java. He recently released Spring: A Developer's Notebook. He spent 13 years at IBM and is now the founder of the J2Life, LLC, consultancy, where he specializes in lightweight development strategies and architectures based on Java technology and Ruby.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Java technology
ArticleID=99262
ArticleTitle=Secrets of lightweight development success, Part 9: Continuations-based frameworks
publish-date=11292005
author1-email=bruce.tate@j2life.com
author1-email-cc=bruce.tate@j2life.com

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers