home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-cocoon-addon-1.4.9-installer.exe / tutor.xml < prev    next >
Encoding:
Extensible Markup Language  |  2004-07-12  |  12.6 KB  |  331 lines

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!--
  3.   Copyright 1999-2004 The Apache Software Foundation
  4.  
  5.   Licensed under the Apache License, Version 2.0 (the "License");
  6.   you may not use this file except in compliance with the License.
  7.   You may obtain a copy of the License at
  8.  
  9.       http://www.apache.org/licenses/LICENSE-2.0
  10.  
  11.   Unless required by applicable law or agreed to in writing, software
  12.   distributed under the License is distributed on an "AS IS" BASIS,
  13.   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.   See the License for the specific language governing permissions and
  15.   limitations under the License.
  16. -->
  17. <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.0//EN" "document-v10.dtd">
  18.  
  19. <document>
  20.   <header>
  21.     <title>Advanced Control Flow</title>
  22.     <authors>
  23.       <person name="Tony Collen" email="tony@apache.org"/>
  24.     </authors>
  25.   </header>
  26.   <body>
  27.  
  28.     <s1 title="Tutorial: A Gentle Introduction to Cocoon Control Flow">
  29.       <p>In this tutorial, we will create a simple number guessing game using
  30.          Cocoon's Control Flow engine.</p>
  31.       <p>After you have Cocoon 2.1 deployed and running, go to where you have
  32.         Cocoon deployed and create a new subdirectory named <code>game</code>.
  33.         Cocoon's default main sitemap will automatically mount the sitemap in
  34.         the subdirectory.</p>
  35.       <p>Create the following <code>sitemap.xmap</code> in the new subdirectory:</p>
  36.       <source><![CDATA[
  37. <?xml version="1.0" encoding="UTF-8"?>
  38. <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
  39.  
  40. <map:components>
  41.   <map:generators default="file">
  42.     <!-- in this example we use JXTemplateGenerator to insert
  43.          Flow variables in page content -->
  44.     <map:generator label="content,data" logger="sitemap.generator.jx"
  45.                    name="jx" src="org.apache.cocoon.generation.JXTemplateGenerator"/>
  46.   </map:generators>
  47.   <map:transformers default="xslt"/>
  48.   <map:serializers default="html"/>
  49.   <map:matchers default="wildcard"/>
  50.   <map:selectors default="browser">
  51.     <map:selector name="exception" src="org.apache.cocoon.selection.XPathExceptionSelector">
  52.       <exception name="invalid-continuation"
  53.                  class="org.apache.cocoon.components.flow.InvalidContinuationException"/>
  54.       <exception class="java.lang.Throwable" unroll="true"/>
  55.     </map:selector>
  56.   </map:selectors>
  57.   <map:actions/>
  58.   <map:pipes default="caching"/>
  59. </map:components>
  60.  
  61. <map:views/>
  62. <map:resources/>
  63. <map:action-sets/>
  64.  
  65. <map:flow language="javascript">
  66.   <!-- Flow will use the javascript functions defined in game.js -->
  67.   <map:script src="flow/game.js"/>
  68. </map:flow>
  69.  
  70. <map:pipelines>
  71.   <map:component-configurations>
  72.     <global-variables/>
  73.   </map:component-configurations>
  74.  
  75.   <map:pipeline>
  76.     <!-- no filename: call main() in game.js -->
  77.     <map:match pattern="">
  78.       <map:call function="main"/>
  79.     </map:match>
  80.  
  81.     <!-- use JXtemplate to generate page content -->
  82.     <map:match pattern="*.jx">
  83.       <map:generate type="jx" src="documents/{1}.jx"/>
  84.       <map:serialize type="xhtml"/>
  85.     </map:match>
  86.  
  87.     <!-- .kont URLs are generated by the Flow system for continuations -->
  88.     <map:match pattern="*.kont">
  89.       <map:call continuation="{1}"/>
  90.     </map:match>
  91.  
  92.     <!-- handle invalid continuations -->
  93.  
  94.     <!-- this style of handling invalidContinuation is now deprecated: -->
  95.     <!-- this URI will never be called automatically anymore. -->
  96.     <!-- see handle-errors below -->
  97.     <map:match pattern="invalidContinuation">
  98.       <map:generate src="documents/invalidContinuation.xml"/>
  99.       <map:serialize type="xml"/>
  100.     </map:match>
  101.  
  102.     <!-- the new non-hardcoded way of handling invalidContinuation -->
  103.     <map:handle-errors>
  104.       <map:select type="exception">
  105.         <map:when test="invalid-continuation">
  106.           <map:generate src="documents/invalidContinuation.html"/>
  107.           <map:serialize type="xhtml"/>
  108.         </map:when>
  109.       </map:select>
  110.     </map:handle-errors>
  111.   </map:pipeline>
  112.  
  113. </map:pipelines>
  114.  
  115. </map:sitemap>
  116. ]]></source>
  117.       <p>Inside the new subdirectory, create two more directories,
  118.         <code>documents/</code> and <code>flow/</code>.</p>
  119.       <p>Inside <code>documents/</code>, you will store the "views" -- pages to
  120.         send to the player. Create the file <code>guess.jx</code>, which will
  121.         be the page the player will enter their guess:</p>
  122.       <source><![CDATA[
  123. <?xml version="1.0"?>
  124. <html xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
  125. <head>
  126.   <title>cocoon flow number guessing game</title>
  127. </head>
  128. <body>
  129.   <h1>Guess the Number Between 1 and 10</h1>
  130.   <h2>${hint}</h2>
  131.   <h3>You've guessed ${guesses} times.</h3>
  132.   <form method="post" action="${cocoon.continuation.id}.kont">
  133.     <input type="text" name="guess"/>
  134.     <input type="submit"/>
  135.   </form>
  136. </body>
  137. </html>
  138. ]]></source>
  139.       <p>
  140.         You'll also need a page to display when the person chooses the correct
  141.         number. Name it <code>success.jx</code> (Again in <code>documents/</code>):
  142.       </p>
  143.       <source><![CDATA[
  144. <?xml version="1.0"?>
  145. <html xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
  146. <head>
  147.   <title>cocoon flow number guessing game</title>
  148. </head>
  149. <body>
  150.   <h1>Success!</h1>
  151.   <h2>The number was: ${random}</h2>
  152.   <h3>It took you ${guesses} tries.</h3>
  153.   <p><a href="./">Play again</a></p>
  154. </body>
  155. </html>
  156. ]]></source>
  157.       <p>
  158.         You may notice some strange codes inside the files -- namely things like
  159.         <code>${random}</code> and <code>${guesses}</code>. They look like
  160.         variables and they will be replaced with values when the pages are
  161.         sent to the client. This is where the
  162.         <link href="jxtemplate.html">JXTemplateGenerator</link> comes in.
  163.       </p>
  164.       <p>
  165.         Inside <code>flow/</code> you will store the code that actually controls
  166.         how this application runs. In the "MVC" pattern the Flow is the
  167.         "Controller" and it is very powerful.
  168.       </p>
  169.       <p>
  170.         Create the following file named <code>game.js</code>:
  171.       </p>
  172.       <source><![CDATA[
  173. function main() {
  174.  
  175.   var random =  Math.round( Math.random() * 9 ) + 1;
  176.  
  177.   var hint = "No hint for you!"
  178.   var guesses = 0;
  179.  
  180.   while (true) {
  181.  
  182.     cocoon.sendPageAndWait("guess.jxt", { "random" : random, "hint" : hint,
  183.                                           "guesses" : guesses} );
  184.  
  185.     var guess = parseInt( cocoon.request.get("guess") );
  186.     guesses++;
  187.  
  188.     if (guess) {
  189.       if (guess > random) {
  190.         hint = "Nope, lower!"
  191.       } else if (guess < random) {
  192.         hint = "Nope, higher!"
  193.       } else {
  194.         break;
  195.       }
  196.     }
  197.   }
  198.  
  199.   cocoon.sendPage("success.jx", {"random" : random, "guess" : guess,
  200.                                   "guesses" : guesses} );
  201. }
  202. ]]></source>
  203.       <p>
  204.         Alright, now let's follow the execution of this Flow and pipeline: The
  205.         player accesses the URL <code>http://host/cocoon/game/</code> and the
  206.         <map:match pattern=""> matches, and starts the pipeline.
  207.       </p>
  208.       <p>
  209.         The function <code>main()</code> which is referenced in
  210.         <code>flow/game.js</code> is called and a new Continuation object is
  211.         created. Without getting into too much detail the state of the Javascript
  212.         code is saved and can be recalled any number of times.
  213.       </p>
  214.       <p>We now enter the code in <code>game.js</code>:</p>
  215.       <p>A random number between 1 and 10 is chosen.</p>
  216.       <p>
  217.         Variables containing a hint for the player and the player's current
  218.         number of guesses are initialized. The Flow now enters the
  219.         <code>while(true)</code> loop which basically keeps the game going until
  220.         the player guesses the correct number.
  221.       </p>
  222.       <p>We now get to the following line, where things start to get interesting:</p>
  223.       <source>
  224. cocoon.sendPageAndWait("guess.jxt", { "random" : random, "hint" : hint, "guesses" : guesses} );
  225. </source>
  226.       <p>
  227.         The Flow layer sends the contents of the URI "guess.jx" which is matched
  228.         in the sitemap (see above). We also pass an inline Javascript object,
  229.         containing three key/value pairs, one named "random" which contains the
  230.         value of the variable random as initialized above, and so on for hint and
  231.         guesses. The keys are substituted later down the line, when the
  232.         <code>JXTemplateGenerator</code> comes into play.
  233.       </p>
  234.       <p>We could also do the following:</p>
  235.       <source>
  236. cocoon.sendPageAndWait("guess.jx", { "foo" : random } );
  237. </source>
  238.       <p>
  239.         In this case, the value of random would be able to be substituted in our
  240.         JXTemplate, but under the name "foo" instead -- we'd just have to make
  241.         sure we have the correct keyname in our template.
  242.       </p>
  243.       <p>
  244.         The Flow Layer also does another interesting thing: it halts the
  245.         execution of the Javascript! Through the magic of continuations the Flow
  246.         Layer is able to resume execution of the script at the exact line in
  247.         which it left off. This creates some very powerful situations with
  248.         respect to web programming, and forces the reader to think very
  249.         differently about how web applications are designed.
  250.       </p>
  251.       <p>
  252.         Picking back up in the script execution, the client is sent through
  253.         the pipeline matching "guess.jx". Referring back to the sitemap, we
  254.         match *.jx, and run the file through the JXTemplateGenerator, which
  255.         substitutes the keynames for the values sent from the
  256.         <link href="api.html#sendPageAndWait">cocoon.sendPageAndWait()</link>
  257.         function.
  258.       </p>
  259.       <p>
  260.         One thing to note is in the form which is sent back to Cocoon when the
  261.         player submits the guess:
  262.       </p>
  263.       <source><![CDATA[
  264. <form method="post" action="${cocoon.continuation.id}.kont">
  265. ]]></source>
  266.       <p>
  267.         Here, ${cocoon.continuation.id} is resolved to a unique identifier which points
  268.         to the current continuation. One can think of this somewhat of a session ID.
  269.       </p>
  270.       <p>
  271.         When the player submits the form, it is submitted to a unique URL which
  272.         contains the continuation ID, plus ".kont", which we end up matching in
  273.         the sitemap:
  274.       </p>
  275.       <source><![CDATA[
  276. <map:match pattern="*.kont">
  277.   <map:call continuation="{1}"/>
  278. </map:match>
  279. ]]></source>
  280.       <p>
  281.         When Cocoon sees a URL like this, it attempts to restart the continuation
  282.         with the specified ID and we re-enter the Javascript code where we left
  283.         off previously.
  284.       </p>
  285.       <p>
  286.         We are now back in the Javascript at the line after
  287.         <link href="api.html#sendPageAndWait">sendPageAndWait()</link>. We create
  288.         a new variable (an int), which we get from the POST request that was sent
  289.         by the form. Notice in the form we had
  290.         <code><input type="text" name="guess"/></code> and in the Javascript
  291.         we get the request parameter by using <code>cocoon.request.get("guess");</code>.
  292.       </p>
  293.       <p>
  294.         Now we increment the player's guess count and we test to see if they
  295.         guessed the correct number. If the guess was too high, we set the hint
  296.         variable telling them to guess lower, we fall through the bottom of
  297.         the while loop and we send the guess form back to the player.
  298.       </p>
  299.       <p>
  300.         If the guess was too low, we tell them to guess higher, we fall through
  301.         the loop as well sending the player the form again.
  302.       </p>
  303.       <p>
  304.         If the guess was correct, we break out of the main loop and send the
  305.         player to a different view, this time to "success.jx", and we give the
  306.         template not only their number and the random number (pointless, yes,
  307.         because they were the same), but also the number of guesses to tell the
  308.         player how good or bad at guessing numbers they are.
  309.       </p>
  310.       <p>
  311.         The main point of interest in the Flow script at this point is the use of
  312.         <code>sendPage()</code> instead of <code>sendPageAndWait()</code>.
  313.         <code>sendPage()</code> works exactly the same, except, yes, you guessed
  314.         it, we don't halt execution of code and keep processing.
  315.       </p>
  316.       <p>At this point there's no more code left and the game is over and the Flow stops.</p>
  317.       <p>
  318.         Another thing to note is the <map:handle-errors> tag in the sitemap.
  319.         Previously, when a continuation which did not exist was called, the Flow
  320.         layer would automatically redirect to the URI "invalidContinuation". Now,
  321.         the Flow layer throws an <code>InvalidContinuationException</code> and
  322.         you can now handle it as described in the handle-errors tag.
  323.       </p>
  324.       <p>
  325.         And that's it! You have now just made your very first application using
  326.         the Flow layer.
  327.       </p>
  328.     </s1>
  329.   </body>
  330. </document>
  331.