Dashboard > RIFE > ... > LiveGuide > GuideNumberguess > Information > Page Comparison
RIFE Log In   View a printable version of the current page.
GuideNumberguess


compared with
Current by Andrés González
on Jun 27, 2006 11:23.

(show comment)
 
Key
These lines were removed. This word was removed.
These lines were added. This word was added.

View page history


There are 1 changes. View first change.

 h1. {anchor:chapter_numberguess}Creating a more advanced RIFE application
  
 h2. {anchor:sect1_numberguess_structure}Laying out the structure
  
 Now we've seen the simplest possible application, and should be ready to move on to a more realistic example. In the real world, we are going to need dynamic and generated content, links and an occasional form that needs to be filled out and processed.
  
 We're going to use a little number guessing game as a slightly simplified real world example. The game lets the user guess a number and repeatedly responds "too high" or "too low", until the guess is correct.
  
 The numberguess example is available in the same RIFE examples file that we used in the previous chapter. Repeat the instructions from the Hello World example to setup RIFE and Jetty for the game.
  
 {noformat}
 cd ~/tutorial/rife-examples-{version}
 mkdir 03_numberguess/WEB-INF/lib
 cp ROOT/WEB-INF/lib/rife-{version}.jar 03_numberguess/WEB-INF/lib/
 mv 03_numberguess ../jetty-{version}/webapps/
 {noformat}
  
 You will need to start Jetty again, you can try the example out at the location [{{*http://localhost:8080/03_numberguess/*}} |http://localhost:8080/03_numberguess/].
  
 The number of files is starting to grow as the application gets more advanced, so let's take a look at the directory structure and files before moving on:
  
 {anchor:example_numberguess_tructure}
  
 {noformat:title=Directory structure for the numberguess example}
 classes/
  rep/
  participants.xml
  
  sites/
  numberguess.xml
  
  elements/
  start.xml
  guess.xml
  success.xml
  
  templates/
  guess.html
  success.html
  
  implementations/
  tutorial/
  numberguess/
  Start.java
  Guess.java
  Success.java
  
 src/
  tutorial/
  numberguess/
  backend/
  Contest.java
  Game.java
 {noformat}
  
 If you look in the {{*elements*}} directory you see three different XML files. Each of them represents an element in the game. The elements are connected by creating execution flows between them in the site definition. These flows are called "Flow links" and are created with the {{*flowlink*}} tag. If you want to pass data between different elements you use "Data links" which are represented by the {{*datalink*}} tag.
  
  
  
 We are going to connect the elements like this:
  
 {anchor:figure_numberguess_site}
  
 *The numberguess site*
  
 !numberguess_site.png!
  
 Elements must provide one or more "exits" if you want to make it possible to link them to other elements. An exit is the starting point of a flow link and the end point is another element.
  
 In [schema above|#figure_numberguess_site], the data links are represented by blue arrows while the flow links are black. We can also see that START has one exit named "started", GUESS has two exits named "start" and "success" and SUCCESS has one exit called "start". By connecting the exit "started" from START to go to GUESS we get the elements linked together.
  
 h2. {anchor:sect1_numberguess_linking}Linking elements
  
 h3. {anchor:sect2_numberguess_exits}Defining exits
  
 As mentioned, the elements have various exits that make it possible to connect the elements to each other. These exits are defined in the element declaration files. For an example, take a look at {{*elements/guess.xml*}}, where two exits, "start" and "success", are defined.
  
 {anchor:example_numberguess_exits}
  
 {code:xml|title=Defining exits for an element}
 <element implementation="tutorial.numberguess.Guess">
  <exit name="start"/>
  <exit name="success"/>
 </element>
 {code}
  
 h3. {anchor:sect2_numberguess_creating_links}Creating the links
  
 The relationships between the three elements, Start, Guess and Success, are defined in the site file, {{*sites/numberguess.xml*}}. It's there that elements are linked together and that the flow of the site is defined. All this is done by the appropriate {{*flowlink*}} tags:
  
 {anchor:example_numberguess_flowlinks}
  
 {code:xml|title=Defining flowlinks}
 <site>
  <element id="Guess" file="guess.xml" url="/guess">
  <flowlink srcexit="start" destid="Start"/>
  <flowlink srcexit="success" destid="Success"/>
  </element>
 </site>
 {code}
  
 This particular element, GUESS, has one link to the START element and one to the SUCCESS element. A flow link is always connected between an exit of an element and an element, and the exits of each element must be specified in the element's definition file.
  
 The benefit of all this is that it's easy to get a clear overview of the relationships and flow between elements in a site just by looking at the site file. This is a great advantage when developing and maintaining web applications, especially big ones.
  
 h2. {anchor:sect1_numberguess_data}Passing data between elements
  
 In addition to setting up flow links, we also need to be able to pass data around between elements. The numberguess game uses a game ID that is used to identify every game that is played. We want to pass this ID around when a game is active. This makes it possible for all the elements to know which game they should work with. Data links are used for this purpose, and their definition is very similar to flow links.
  
 h3. {anchor:sect2_numberguess_inputs_outputs}Defining inputs and outputs
  
 In order to be able to pass input variables to and from an element you need to define inputs and outputs for those variables. To do that, you use {{*input*}} and {{*output*}} tags in the element declaration. After adding the input and output tags to the {{*Guess*}} element declaration, the result is:
  
 {anchor:example_numberguess_inputs_outputs}
  
 {code:xml|title=Defining inputs and outputs}
 <element implementation="tutorial.numberguess.Guess">
  <input name="gameid"/>
  
  <exit name="start"/>
  <exit name="success"/>
  
  <output name="gameid"/>
 </element>
 {code}
  
 h3. {anchor:sect2_numberguess_creating_datalinks}Creating the data link
  
 To actually connect the output of one element to the input of another, a data link must be created. An example from the number guess game is the link from the "gameid" output of the {{*Guess*}} element that is connected to the "gameid" input of the {{*Start*}} element:
  
 {code:xml}
 <datalink srcoutput="gameid" destid="Start" destinput="gameid"/>
 {code}
  
 After adding datalinks to the example from above, we would get the following:
  
 {anchor:example_numberguess_guess_datalinks}
  
 {code:xml|title=Adding data links to the guess element}
 <site>
  <element id="Guess" file="guess.xml" url="guess">
  <flowlink srcexit="start" destid="Start"/>
  <datalink srcoutput="gameid" destid="Start" destinput="gameid"/>
  
  <flowlink srcexit="success" destid="Success"/>
  <datalink srcoutput="gameid" destid="Success" destinput="gameid"/>
  </element>
 </site>
 {code}
  
 Note that the output name doesn't have to be the same as the input name, since you provide them both to the {{*datalink*}} tag.
  
 h2. {anchor:sect1_numberguess_templates}Templates
  
 In order to separate the presentation and the data layer, RIFE uses templates. A template is a regular HTML file with specific markup to structure it for web application usage. There's no template scripting language and it's not possible to include any logic at all inside them. They only contain blocks and placeholders for content: values. The logic is placed in the elements where blocks are manipulated and content assigned or constructed in the placeholders. The blocks and value tags are replaced by the generated output before being sent to the browser.
  
 This makes RIFE templates different from other solutions, for example JSP, since the pages can be edited in any regular HTML editor. Since the files are valid HTML, they can also easily be tried out in a browser without having to run the whole application.
  
 In the Hello World example, all the content was generated directly in the Java class implementation, by writing it from the {{*processElement*}} method. It would be pretty cumbersome if all content would need to be generated in this fashion. It is here that templates come in handy.
  
 In the numberguess application, there are two templates: one for the guess page and one for the success page. A quick look at the guess template gives the following snippet:
  
 {anchor:example_numberguess_template}
  
 {code:xml|title=The template for the guess page}
 <p>Guess a number from 0 to 100</p>
 <!--V 'warning'--><!--/V-->
 <!--B 'invalid'-->
 <p><font color="#990000">The guess is invalid.</font></p>
 <!--/B-->
 <p><i><!--V 'indication'--><!--/V--></i></p>
 <!--B 'lower'-->The answer is lower.<!--/B-->
 <!--B 'higher'-->The answer is higher.<!--/B-->
 {code}
  
 As you can see, RIFE makes use of ordinary HTML comments. There is also another syntax for template tags, that is a bit less verbose where {{*\[\!*}} is used instead of {{*<!--*}} and {{*\]*}} instead of {{*-->*}}. You can see the two tags {{*V*}} and {{*B*}} being used, where {{*V*}} is a placeholder for a value and {{*B*}} is the definition of a block. To insert a string into a value tag from the Java element code might look like this:
  
 {code:java}
 template.setValue("warning", "The answer is lower.");
 {code}
  
 In this example though, we use the blocks that are defined with {{*B*}} tags and insert one of them as the content of a value:
  
 {code:java}
  template.setBlock("warning", "lower");
  template.setBlock("indication", "lower");
 {code}
  
 This way we don't have to use one single string of HTML inside the Java code, and no logic in the templates. This very clear separation of content and logic is one of RIFE's great strengths.
  
 h2. {anchor:sect1_numbergess_forms}Reading data from a form
  
 Since we need to handle input from the user we have a form on the web page, with an entry and a submit button. This is reflected in the Guess element definition by the submission tag:
  
 {anchor:example_numberguess_form}
  
 {code:xml|title=Adding a submission to the guess element}
 <element implementation="tutorial.numberguess.Guess">
  <input name="gameid"/>
  
  <submission name="performGuess">
  <param name="guess">
  <default>-1</default>
  </param>
  </submission>
  
  <exit name="start"/>
  <exit name="success"/>
  
  <output name="gameid"/>
 </element>
 {code}
  
 Here, we have defined one submission parameter, namely the number that the user guessed. Submissions always refer back to the original element. This centralizes all the handling of a submission in the same element, instead of having one page for showing the form and another one that deals with the submitted data.
  
 Since the form display and data processing is done in the same element, it's easy to just show the form again if some data is missing or invalid, without having to ask the user to go back to the previous page to fill out the rest.
  
 We also need to add a bit of code to the template to handle the submission form:
  
 {anchor:example_numberguess_form_template}
  
 {code:xml|title=The guess template with a form}
 <form action="[!V 'SUBMISSION:FORM:performGuess'/]" method="post">
  <!--V 'SUBMISSION:PARAMS:performGuess'/-->
  <input type="text" name="guess" value="[!V 'PARAM:guess'][!/V]" /><br />
  <input type="submit" value="Guess" /><br />
 </form>
 {code}
  
 The action is set to a special value: {{*SUBMISSION:FORM:performGuess*}}. RIFE replaces this value with the correct URL, in this case the URL that corresponds to the {{*GUESS*}} element. The last part of this special value refers to the name of the submission that you declared earlier in the element XML definition.
  
 The value of the text input HTML tag is set to the {{*PARAM:guess*}} value. This again corresponds to the {{*guess*}} parameter of your submission definition. This value is automatically replaced by the last value of the parameter and allows the user to always see the last guess that was made. Note that we use a a longer value tag here and we'll explain its use in detail later. All you have to know now is that when no {{*guess*}} parameter is available, the value will be replaced with an empty string and thus show up as an empty field to the user.
  
 {warning:title=Warning}
 You always have to use {{*SUBMISSION:FORM:name*}} together with {{*SUBMISSION:PARAMS:name*}}. RIFE will warn you about this, but it's always good to know beforehand.
 {warning}
  
 Values with a prefix like {{*SUBMISSION:*}} are treated specially by RIFE. Here, for instance, the form action and parameters are set up. There are other prefixes such as {{*EXIT:*}} and {{*CONFIG:*}} as we will see later on. These tags are called [filtered value tags|http://rifers.org/wiki/display/RIFE/Filtered+values+tags] since RIFE automatically filters them out and replaces them with special content that's related to the application.
  
 When the page is entered, the element needs to check if a submission exists, and if so, handle it. The check is done through the {{*hasSubmission*}} method and values are fetched with the various methods that start with {{*getParameter*}}. To get an integer value, {{*getParameterInt*}} is used, and so on. The complete list of methods can be found in the API reference documentation, under the class {{*com.uwyn.rife.engine.Element*}}.
  
 {anchor:example_numberguess_form_java}
  
 {code:java|title=Java code for the Guess element}
 package tutorial.numberguess;
  
 import com.uwyn.rife.engine.Element;
 import com.uwyn.rife.template.Template;
 import tutorial.numberguess.backend.Contest;
 import tutorial.numberguess.backend.Game;
  
 public class Guess extends Element
 {
  private Template mTemplate = null;
  
  private void handleGuess(Game game)
  {
  int guess = getParameterInt("guess", -1);
  if (guess < 0 || guess > 100)
  {
  mTemplate.setBlock("warning", "invalid");
  return;
  }
  
  game.increaseGuesses();
  
  if (game.getAnswer() < guess)
  {
  mTemplate.setBlock("indication", "lower");
  }
  else if (game.getAnswer() > guess)
  {
  mTemplate.setBlock("indication", "higher");
  }
  else
  {
  setOutput("gameid", getInput("gameid"));
  exit("success");
  }
  }
  
  public void processElement()
  {
  Game game = Contest.getGame(getInput("gameid"));
  if (null == game)
  {
  exit("start");
  }
  
  mTemplate = getHtmlTemplate("guess");
  
  if (hasSubmission("performGuess"))
  {
  handleGuess(game);
  }
  
  print(mTemplate);
  }
 }
 {code}
  
 In [this Java code|#example_numberguess_form_java] we see quite a few interesting parts. Let's start with the call to {{*exit*}} in {{*processElement*}}. This instructs RIFE to activate the flow link that's connected to the {{*start*}} exit. No further processing is done in the element, it will immediately be exited and the logic will proceed with the element that's being pointed to by the flow link. In this particular example, it causes the start page to be shown when no active game could be found.
  
 If a game was successfully retrieved, a check is done to see if the request contains the submission named "perform_guess". If it does, {{*handleGuess*}} is called to see if the user's guess is correct. If it is, the exit named "success" is triggered and the element connected to this exit will handle the output.
  
 If no submission was found, the default template will be displayed, which asks the user to enter a guess.
  
 h2. {anchor:sect1_numberguess_arrival}Specifying the starting point
  
 It would be nice if our users wouldn't have to enter the location {{http://localhost:8080/03_numberguess/start/}} to enter our game, but instead just {{http://localhost:8080/03_numberguess/}}.
  
 We can achieve this by specifying an arrival element for the site. An arrival element is the element that is used for the root of the application, and is setup with the {{*arrival*}} tag. After this final addition, the site file will look like the following:
  
 {anchor:example_number_guess_site}
  
 {code:xml|title=The final number guess site file}
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE site SYSTEM "/dtd/site.dtd">
  
 <site>
  <arrival destid="Start"/>
  
  <element id="Start" file="start.xml" url="/start">
  <flowlink srcexit="started" destid="Guess"/>
  <datalink srcoutput="gameid" destid="Guess" destinput="gameid"/>
  </element>
  
  <element id="Guess" file="guess.xml" url="/guess">
  <flowlink srcexit="start" destid="Start"/>
  <datalink srcoutput="gameid" destid="Start" destinput="gameid"/>
  
  <flowlink srcexit="success" destid="Success"/>
  <datalink srcoutput="gameid" destid="Success" destinput="gameid"/>
  </element>
  
  <element id="Success" file="success.xml">
  <flowlink srcexit="start" destid="Start"/>
  </element>
 </site>
 {code}
  
 h2. {anchor:sect1_numberguess_wrap_up}Wrapping it up
  
 Now we've written a simple web game, and hopefully learnt a lot about RIFE in the process. We've defined flow and data links, used templates to create web pages, and handled user input from forms. We should now be ready to move on to more advanced topics, like [writing complex templates|GuideTemplates].


Are you enjoying Confluence? Please consider purchasing it today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.2.1a Build:#515 May 19, 2006) - Bug/feature request - Contact Administrators