You are viewing a plain text version of this content. The canonical link for it is here.
Posted to docs@cocoon.apache.org by da...@cocoon.zones.apache.org on 2007/06/07 19:15:07 UTC
[DAISY] Updated: Tutorial: A Gentle Introduction to Cocoon Control
Flow
A document has been updated:
http://cocoon.zones.apache.org/daisy/documentation/1241.html
Document ID: 1241
Branch: main
Language: default
Name: Tutorial: A Gentle Introduction to Cocoon Control Flow (previously Samples)
Document Type: Cocoon Document (unchanged)
Updated on: 6/7/07 5:15:04 PM
Updated by: Reinhard Pötz
A new version has been created, state: publish
Parts
=====
Content
-------
This part has been updated.
Mime type: text/xml (unchanged)
File name: (unchanged)
Size: 10295 bytes (previous version: 37 bytes)
Content diff:
--- <html><body><p>TODO</p></body></html>
+++ <html>
+++ <body>
+++
+++ <p class="warn">This tutorial was converted from having Cocoon 2.1 users as
+++ target audience to Cocoon 2.2 users but hasn't been verified if everything works
+++ as expected yet.</p>
+++
+++ <h1>Overview</h1>
+++
+++ <p>In this tutorial, we will create a simple number guessing game using
+++ <a href="daisy:1372">Cocoon's Control Flow engine</a>.</p>
+++
+++ <p>After you have the <a href="daisy:1159">Your first Cocoon application using
+++ Maven 2</a> tutorial running, create a new subdirectory, open the blocks root
+++ sitemap <tt>block1\src\main\resources\COB-INF\sitemap.xmap)</tt></p>
+++
+++ <h1>Sitemap</h1>
+++
+++ <p>Create the following <tt>sitemap.xmap</tt> in the new subdirectory:</p>
+++
+++ <pre><?xml version="1.0" encoding="UTF-8"?>
+++ <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
+++
+++ <map:pipelines>
+++
+++ <map:pipeline id="flow-sample">
+++ <!-- no filename: call main() in game.js -->
+++ <map:match pattern="game">
+++ <map:call function="main"/>
+++ </map:match>
+++
+++ <!-- use JXtemplate to generate page content -->
+++ <map:match pattern="*.jx" internal-only="true">
+++ <map:generate type="jx" src="documents/{1}.jx"/>
+++ <map:serialize type="xhtml"/>
+++ </map:match>
+++
+++ <!-- .kont URLs are generated by the Flow system for continuations -->
+++ <map:match pattern="*.kont">
+++ <map:call continuation="{1}"/>
+++ </map:match>
+++
+++ <!-- handle invalid continuations -->
+++
+++ <!-- this style of handling invalidContinuation is now deprecated: -->
+++ <!-- this URI will never be called automatically anymore. -->
+++ <!-- see handle-errors below -->
+++ <map:match pattern="invalidContinuation">
+++ <map:generate src="documents/invalidContinuation.xml"/>
+++ <map:serialize type="xml"/>
+++ </map:match>
+++
+++ <!-- the new non-hardcoded way of handling invalidContinuation -->
+++ <map:handle-errors>
+++ <map:select type="exception">
+++ <map:when test="invalid-continuation">
+++ <map:generate src="documents/invalidContinuation.html"/>
+++ <map:serialize type="xhtml"/>
+++ </map:when>
+++ </map:select>
+++ </map:handle-errors>
+++ </map:pipeline>
+++
+++ </map:pipelines>
+++
+++ </map:sitemap>
+++ </pre>
+++
+++ <h1>View</h1>
+++
+++ <p>In <tt>block1\src\main\resources\COB-INF</tt>, create a subdirectory,
+++ <tt>documents/</tt>.</p>
+++
+++ <p>Inside <tt>documents/</tt>, you will store the "views" -- pages to send to
+++ the player. Create the file <tt>guess.jx</tt>, which will be the page the player
+++ will enter their guess:</p>
+++
+++ <pre><?xml version="1.0"?>
+++ <html xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
+++ <head>
+++ <title>cocoon flow number guessing game</title>
+++ </head>
+++ <body>
+++ <h1>Guess the Number Between 1 and 10</h1>
+++ <h2>${hint}</h2>
+++ <h3>You've guessed ${guesses} times.</h3>
+++ <form method="post" action="${cocoon.continuation.id}.kont">
+++ <input type="text" name="guess"/>
+++ <input type="submit"/>
+++ </form>
+++ </body>
+++ </html>
+++ </pre>
+++
+++ <p>You'll also need a page to display when the person chooses the correct
+++ number. Name it <tt>success.jx</tt> (Again in <tt>documents/</tt>):</p>
+++
+++ <pre><?xml version="1.0"?>
+++ <html xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
+++ <head>
+++ <title>cocoon flow number guessing game</title>
+++ </head>
+++ <body>
+++ <h1>Success!</h1>
+++ <h2>The number was: ${random}</h2>
+++ <h3>It took you ${guesses} tries.</h3>
+++ <p><a href="./">Play again</a></p>
+++ </body>
+++ </html>
+++ </pre>
+++
+++ <p>You may notice some strange codes inside the files -- namely things like
+++ <tt>${random}</tt> and <tt>${guesses}</tt>. They look like variables and they
+++ will be replaced with values when the pages are sent to the client. This is
+++ where the <a href="daisy:511">JXTemplateGenerator</a> comes in.</p>
+++
+++ <h1>Controller</h1>
+++
+++ <p>Inside <tt>flow/</tt> you will store the code that actually controls how this
+++ application runs. In the "MVC" pattern the Flow is the "Controller" and it is
+++ very powerful.</p>
+++
+++ <p>Create the following file named <tt>game.js</tt>:</p>
+++
+++ <pre>function main() {
+++
+++ var random = Math.round( Math.random() * 9 ) + 1;
+++
+++ var hint = "No hint for you!"
+++ var guesses = 0;
+++
+++ while (true) {
+++
+++ cocoon.sendPageAndWait("guess.jxt",
+++ {
+++ "random" : random,
+++ "hint" : hint,
+++ "guesses" : guesses
+++ }
+++ );
+++
+++ var guess = parseInt( cocoon.request.get("guess") );
+++ guesses++;
+++
+++ if (guess) {
+++ if (guess > random) {
+++ hint = "Nope, lower!"
+++ } else if (guess < random) {
+++ hint = "Nope, higher!"
+++ } else {
+++ break;
+++ }
+++ }
+++ }
+++
+++ cocoon.sendPage("success.jx",
+++ {
+++ "random" : random,
+++ "guess" : guess,
+++ "guesses" : guesses
+++ }
+++ );
+++ }
+++ </pre>
+++
+++ <p>Alright, now let's follow the execution of this Flow and pipeline: The player
+++ accesses the URL <tt>http://host/cocoon/game/</tt> and the <map:match
+++ pattern=""> matches, and starts the pipeline.</p>
+++
+++ <p>The function <tt>main()</tt> which is referenced in <tt>flow/game.js</tt> is
+++ called and a new Continuation object is created. Without getting into too much
+++ detail the state of the Javascript code is saved and can be recalled any number
+++ of times.</p>
+++
+++ <p>We now enter the code in <tt>game.js</tt>:</p>
+++
+++ <p>A random number between 1 and 10 is chosen.</p>
+++
+++ <p>Variables containing a hint for the player and the player's current number of
+++ guesses are initialized. The Flow now enters the <tt>while(true)</tt> loop which
+++ basically keeps the game going until the player guesses the correct number.</p>
+++
+++ <p>We now get to the following line, where things start to get interesting:</p>
+++
+++ <pre>cocoon.sendPageAndWait("guess.jxt", { "random" : random, "hint" : hint, "guesses" : guesses} );
+++ </pre>
+++
+++ <p>The Flow layer sends the contents of the URI "guess.jx" which is matched in
+++ the sitemap (see above). We also pass an inline Javascript object, containing
+++ three key/value pairs, one named "random" which contains the value of the
+++ variable random as initialized above, and so on for hint and guesses. The keys
+++ are substituted later down the line, when the <tt>JXTemplateGenerator</tt>
+++ comes into play.</p>
+++
+++ <p>We could also do the following:</p>
+++
+++ <pre>cocoon.sendPageAndWait("guess.jx", { "foo" : random } );
+++ </pre>
+++
+++ <p>In this case, the value of random would be able to be substituted in our
+++ JXTemplate, but under the name "foo" instead -- we'd just have to make sure we
+++ have the correct keyname in our template.</p>
+++
+++ <p>The Flow Layer also does another interesting thing: it halts the execution of
+++ the Javascript! Through the magic of continuations the Flow Layer is able to
+++ resume execution of the script at the exact line in which it left off. This
+++ creates some very powerful situations with respect to web programming, and
+++ forces the reader to think very differently about how web applications are
+++ designed.</p>
+++
+++ <p>Picking back up in the script execution, the client is sent through the
+++ pipeline matching "guess.jx". Referring back to the sitemap, we match *.jx, and
+++ run the file through the JXTemplateGenerator, which substitutes the keynames for
+++ the values sent from the
+++ <a href="daisy:518#sendPageAndWait">cocoon.sendPageAndWait()</a> function.</p>
+++
+++ <p>One thing to note is in the form which is sent back to Cocoon when the player
+++ submits the guess:</p>
+++
+++ <pre><form method="post" action="${cocoon.continuation.id}.kont">
+++ </pre>
+++
+++ <p>Here, ${cocoon.continuation.id} is resolved to a unique identifier which
+++ points to the current continuation. One can think of this somewhat of a session
+++ ID.</p>
+++
+++ <p>When the player submits the form, it is submitted to a unique URL which
+++ contains the continuation ID, plus ".kont", which we end up matching in the
+++ sitemap:</p>
+++
+++ <pre><map:match pattern="*.kont">
+++ <map:call continuation="{1}"/>
+++ </map:match>
+++ </pre>
+++
+++ <p>When Cocoon sees a URL like this, it attempts to restart the continuation
+++ with the specified ID and we re-enter the Javascript code where we left off
+++ previously.</p>
+++
+++ <p>We are now back in the Javascript at the line after
+++ <a href="daisy:518#sendPageAndWait">sendPageAndWait()</a>. We create a new
+++ variable (an int), which we get from the POST request that was sent by the form.
+++ Notice in the form we had <tt><input type="text" name="guess"/></tt> and
+++ in the Javascript we get the request parameter by using
+++ <tt>cocoon.request.get("guess");</tt>.</p>
+++
+++ <p>Now we increment the player's guess count and we test to see if they guessed
+++ the correct number. If the guess was too high, we set the hint variable telling
+++ them to guess lower, we fall through the bottom of the while loop and we send
+++ the guess form back to the player.</p>
+++
+++ <p>If the guess was too low, we tell them to guess higher, we fall through the
+++ loop as well sending the player the form again.</p>
+++
+++ <p>If the guess was correct, we break out of the main loop and send the player
+++ to a different view, this time to "success.jx", and we give the template not
+++ only their number and the random number (pointless, yes, because they were the
+++ same), but also the number of guesses to tell the player how good or bad at
+++ guessing numbers they are.</p>
+++
+++ <p>The main point of interest in the Flow script at this point is the use of
+++ <tt>sendPage()</tt> instead of <tt>sendPageAndWait()</tt>. <tt>sendPage()</tt>
+++ works exactly the same, except, yes, you guessed it, we don't halt execution of
+++ code and keep processing.</p>
+++
+++ <p>At this point there's no more code left and the game is over and the Flow
+++ stops.</p>
+++
+++ <h1>Model</h1>
+++
+++ <p>TBD</p>
+++
+++ <h1>Error Handling</h1>
+++
+++ <p>Another thing to note is the <map:handle-errors> tag in the sitemap.
+++ Previously, when a continuation which did not exist was called, the Flow layer
+++ would automatically redirect to the URI "invalidContinuation". Now, the Flow
+++ layer throws an <tt>InvalidContinuationException</tt> and you can now handle it
+++ as described in the handle-errors tag.</p>
+++
+++ <p>And that's it! You have now just made your very first application using the
+++ Flow layer.</p>
+++
+++ </body>
+++ </html>