The FreePastry Tutorial.
This tutorial is designed to get you cooking quickly with the FreePastry
API and software toolkit.
Version @tutorial_version@; @tutorial_date@. For FreePastry version @freepastry_version@. Maintained by @maintainer@.
Lesson 0.a
Continuations.
FreePastry and its applications use Continuations in various places, so it is important that you understand what they do and how to use them.A continuation is similar to a callback, or in java, a Listener, but it is typically used only once. A google search for continuations will show you a variety of uses in computer science, but the primary use for them in FreePastry is to handle network latency, or other types of IO that may block or take a significant amount of time.
To understand why we use continuations, let's look at an alternative style of calls. RMI/RPC style calls are generally blocking calls. Blocking calls work similar to a regular function call. The lookup command in Past (FreePastry's DHT) could look like:
public Result lookup(Id id) { // blocking network call }But if the network call takes a while to execute due to latency, then your program will block until it completes. In the meantime you could be doing other lookups, but because of this style of call, you are forced to wait, or use a lot of threads. Continuations allow you to "continue" processing when the result arrives, and in the meantime issue other requests.
Let's look at the
rice.Continuation
interface.
/** * Asynchronously receives the result to a given method call, using * the command pattern. * * Implementations of this class contain the remainder of a computation * which included an asynchronous method call. When the result to the * call becomes available, the receiveResult method on this command * is called. * * @version $Id: index.html 2716 2005-08-10 17:42:38 +0200 (Wed, 10 Aug 2005) jeffh $ * * @author Alan Mislove * @author Andreas Haeberlen */ public interface Continuation { /** * Called when a previously requested result is now availble. * * @param result The result of the command. */ public void receiveResult(Object result); /** * Called when an execption occured as a result of the * previous command. * * @param result The exception which was caused. */ public void receiveException(Exception result); }In Past (the DHT that runs on top of FreePastry) the code to do a lookup looks like this:
public void lookup(Id id, Continuation command) { // non-blocking network call }Note the differences between this call and the alternative approach mentioned before:
- The method takes a Continuation.
- There is no return value.
command.receiveResult()
with the response. Inside the continuation, you can complete the task that required the lookup. If instead, an error occurs, Past will call command.receiveException()
. Note that because receiveResult()
is called with an Object, you must cast the result to what you are expecting from the lookup.Here is an example implementation of a continuation MyContinuation.java:
First, create the continuation :
class MyContinuation implements Continuation { /** * Called when the result arrives. */ public void receiveResult(Object result) { PastContent pc = (PastContent)result; System.out.println("Received a "+pc); } /** * Called if there is an error. */ public void receiveException(Exception result) { System.out.println("There was an error: "+result); } }Note that the continuation has 2 methods it must implement:
receiveResult()
and receiveException()
. This continuation will print "Received a blah" when the result comes in. Note that we need to cast the result to what we should be expecting. In the case of Past, we know that the result will be a PastContent. If there is an error, we will print "There was an error: Description of the error"Here is the code to use our continuation. Note that this code will not run, because Past and Id have not been initialized. (You will learn how to do that in Lesson 7).
Download the code here: TestContinuation.java
public class TestContinuation { public static void main(String[] args) { Past past = null; // generated elsewhere Id id = null; // generated elsewhere // create the continuation Continuation command = new MyContinuation(); // make the call with the continuation past.lookup(id, command); } }This code calls lookup with the continuation. When the result arrives,
command.receiveResult()
will be called.Anonymous inner classes
Java hackers love anonymous inner classes, so here is the same code as an anonymous inner class, rather than a seperate class. This is handy because it flows a bit more like a typical blocking program. First do apast.lookup()
then System.out.println()
.Download the code here: TestContinuationAnon.java
public class TestContinuationAnon { public static void main(String[] args) { Past past = null; // generated elsewhere Id id = null; // generated elsewhere // same code as TestContinuation and MyContinuation combined past.lookup(id, new Continuation() { // will be called if success in the lookup public void receiveResult(Object result) { PastContent pc = (PastContent)result; System.out.println("Received a "+pc); } // will be called if failure in the lookup public void receiveException(Exception result) { System.out.println("There was an error: "+result); } }); } }Click here for more information on Java Anonymous Inner Classes.