Este contenido no está disponible en el idioma seleccionado.

1.5. Seam pageflow: the numberguess example


For Seam applications with freeform (ad hoc) navigation, JSF/Seam navigation rules are a good way to define page flow. However, in applications with more constrained navigation styles, especially user interfaces that are more stateful, navigation rules make understanding system flow difficult. Combining information from view pages, actions, and navigation rules makes this flow easier to understand.
With Seam, jPDL process definition can be used to define pageflow, as seen in the number guessing example that follows.

1.5.1. Understanding the code

The example uses one JavaBean, three JSP pages and a jPDL pageflow definition. We will start by looking at the pageflow:

Example 1.23. pageflow.jpdl.xml

<pageflow-definition 
    xmlns="http://jboss.com/products/seam/pageflow"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://jboss.com/products/seam/pageflow 
                        http://jboss.com/products/seam/pageflow-2.2.xsd"
    name="numberGuess">
  
  <start-page name="displayGuess" view-id="/numberGuess.jspx">                        
    <redirect/>
    <transition name="guess" to="evaluateGuess">                                      
      <action expression="#{numberGuess.guess}"/>                                     
    </transition>
    <transition name="giveup" to="giveup"/>
    <transition name="cheat" to="cheat"/>
  </start-page>
  
  <decision name="evaluateGuess" expression="#{numberGuess.correctGuess}">            
    <transition name="true" to="win"/>
    <transition name="false" to="evaluateRemainingGuesses"/>
  </decision>
  
  <decision name="evaluateRemainingGuesses" expression="#{numberGuess.lastGuess}">
    <transition name="true" to="lose"/>
    <transition name="false" to="displayGuess"/>
  </decision>
  
  <page name="giveup" view-id="/giveup.jspx">
    <redirect/>
    <transition name="yes" to="lose"/>
    <transition name="no" to="displayGuess"/>
  </page>
  
  <process-state name="cheat">
    <sub-process name="cheat"/>
    <transition to="displayGuess"/>
  </process-state>
  
  <page name="win" view-id="/win.jspx">
    <redirect/>
    <end-conversation/>
  </page>
  
  <page name="lose" view-id="/lose.jspx">
    <redirect/>
    <end-conversation/>
  </page>
  
</pageflow-definition>
Copy to Clipboard Toggle word wrap

1

The <page> element defines a wait state where the system displays a particular JSF view and waits for user input. The view-id is the same JSF view id used in plain JSF navigation rules. The redirect attribute tells Seam to use post-then-redirect when navigating to the page. (This results in friendly browser URLs.)

2

The <transition> element names a JSF outcome. The transition is triggered when a JSF action results in that outcome. Execution will then proceed to the next node of the pageflow graph, after invocation of any jBPM transition actions.

3

A transition <action> is just like a JSF action, except that it occurs when a jBPM transition occurs. The transition action can invoke any Seam component.

4

A <decision> node branches the pageflow, and determines the next node to execute by evaluating a JSF EL expression.
In the JBoss Developer Studio pageflow editor, the pageflow looks like this:
With that pageflow in mind, the rest of the application is much easier to understand.
Here is the main page of the application, numberGuess.jspx:

Example 1.24. numberGuess.jspx

<?xml version="1.0"?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" 
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:s="http://jboss.com/products/seam/taglib"
          xmlns="http://www.w3.org/1999/xhtml"
          version="2.0">
  <jsp:output doctype-root-element="html" 
              doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
              doctype-system=
              "http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
  <jsp:directive.page contentType="text/html"/>
  <html>
    <head>
      <title>Guess a number...</title>
      <link href="niceforms.css" rel="stylesheet" type="text/css" />
      <script language="javascript" type="text/javascript" 
              src="niceforms.js" />
    </head>
    <body>
      <h1>Guess a number...</h1>
      <f:view>
        <h:form styleClass="niceform">
          
          <div>
            <h:messages globalOnly="true"/>
            <h:outputText value="Higher!" 
                          rendered="#{
                                    numberGuess.randomNumber gt 
                                    numberGuess.currentGuess}"/>
            <h:outputText value="Lower!" 
                          rendered="#{
                                    numberGuess.randomNumber lt 
                                    numberGuess.currentGuess}"/>
          </div>
          
          <div>
            I'm thinking of a number between 
            <h:outputText value="#{numberGuess.smallest}"/> and 
            <h:outputText value="#{numberGuess.biggest}"/>. You have 
            <h:outputText value="#{numberGuess.remainingGuesses}"/> 
            guesses.
          </div>
          
          <div>
            Your guess: 
            <h:inputText value="#{numberGuess.currentGuess}" 
                         id="inputGuess" required="true" size="3" 
                         rendered="#{
                                   (numberGuess.biggest-numberGuess.smallest) gt 
                                   20}">
              <f:validateLongRange maximum="#{numberGuess.biggest}" 
                                   minimum="#{numberGuess.smallest}"/>
            </h:inputText>
            <h:selectOneMenu value="#{numberGuess.currentGuess}" 
                             id="selectGuessMenu" required="true"
                             rendered="#{
                                       (numberGuess.biggest-numberGuess.smallest) le 
                                       20 and 
                                       (numberGuess.biggest-numberGuess.smallest) gt 
                                       4}">
              <s:selectItems value="#{numberGuess.possibilities}" 
                             var="i" label="#{i}"/>
            </h:selectOneMenu>
            <h:selectOneRadio value="#{numberGuess.currentGuess}" 
                              id="selectGuessRadio" 
                              required="true"
                              rendered="#{
                                        (numberGuess.biggest-numberGuess.smallest) le 
                                        4}">
              <s:selectItems value="#{numberGuess.possibilities}" 
                             var="i" label="#{i}"/>
            </h:selectOneRadio>
            <h:commandButton value="Guess" action="guess"/>
            <s:button value="Cheat" view="/confirm.jspx"/>
            <s:button value="Give up" action="giveup"/>
          </div>
          
          <div>
            <h:message for="inputGuess" style="color: red"/>
          </div>
          
        </h:form>
      </f:view>
    </body>
  </html>
</jsp:root>
Copy to Clipboard Toggle word wrap
Note that the command button names the guess transition instead of calling an action directly.
The win.jspx page is predictable:

Example 1.25. win.jspx

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" 
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns="http://www.w3.org/1999/xhtml"
          version="2.0">
  <jsp:output doctype-root-element="html"
              doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
              doctype-system="http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
  <jsp:directive.page contentType="text/html"/>
  <html>
    <head>
      <title>You won!</title>
      <link href="niceforms.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
      <h1>You won!</h1>
      <f:view>
        Yes, the answer was 
        <h:outputText value="#{numberGuess.currentGuess}" />.
        It took you 
        <h:outputText value="#{numberGuess.guessCount}" /> guesses.
        <h:outputText value="But you cheated, so it doesn't count!" 
                      rendered="#{numberGuess.cheat}"/>
        Would you like to <a href="numberGuess.seam">play again</a>?
      </f:view>
    </body>
  </html>
</jsp:root>
Copy to Clipboard Toggle word wrap
The lose.jspx is very similar, so we have not included it here.
Finally, the application code is as follows:

Example 1.26. NumberGuess.java

@Name("numberGuess")
@Scope(ScopeType.CONVERSATION)
public class NumberGuess implements Serializable {
   
   private int randomNumber;
   private Integer currentGuess;
   private int biggest;
   private int smallest;
   private int guessCount;
   private int maxGuesses;
   private boolean cheated;
   
   @Create                                                   
   public void begin()
   {
      randomNumber = new Random().nextInt(100);
      guessCount = 0;
      biggest = 100;
      smallest = 1;
   }
   
   public void setCurrentGuess(Integer guess)
   {
      this.currentGuess = guess;
   }
   
   public Integer getCurrentGuess()
   {
      return currentGuess;
   }
   
   public void guess()
   {
      if (currentGuess>randomNumber)
      {
         biggest = currentGuess - 1;
      }
      if (currentGuess<randomNumber)
      {
         smallest = currentGuess + 1;
      }
      guessCount ++;
   }
   
   public boolean isCorrectGuess()
   {
      return currentGuess==randomNumber;
   }
   
   public int getBiggest()
   {
      return biggest;
   }
   
   public int getSmallest()
   {
      return smallest;
   }
   
   public int getGuessCount()
   {
      return guessCount;
   }
   
   public boolean isLastGuess()
   {
      return guessCount==maxGuesses;
   }

   public int getRemainingGuesses() {
      return maxGuesses-guessCount;
   }

   public void setMaxGuesses(int maxGuesses) {
      this.maxGuesses = maxGuesses;
   }

   public int getMaxGuesses() {
      return maxGuesses;
   }

   public int getRandomNumber() {
      return randomNumber;
   }

   public void cheated()
   {
      cheated = true;
   }
   
   public boolean isCheat() {
      return cheated;
   }
   
   public List<Integer> getPossibilities()
   {
      List<Integer> result = new ArrayList<Integer>();
      for(int i=smallest; i<=biggest; i++) result.add(i);
      return result;
   }
   
}
Copy to Clipboard Toggle word wrap

1

The first time a JSP page asks for a numberGuess component, Seam will create a new one for it, and the @Create method will be invoked, allowing the component to initialize itself.
The pages.xml file starts a Seam conversation, and specifies the pageflow definition to use for the conversation's page flow. Refer to Chapter 8, Conversations and workspace management for more information.

Example 1.27. pages.xml

<?xml version="1.0" encoding="UTF-8"?>
<pages xmlns="http://jboss.com/products/seam/pages"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd">
  <page view-id="/numberGuess.jspx">
    <begin-conversation join="true" pageflow="numberGuess"/>
  </page>
</pages>
Copy to Clipboard Toggle word wrap
This component is pure business logic. Since it requires no information about the user interaction flow, it is potentially more reuseable.
Volver arriba
Red Hat logoGithubredditYoutubeTwitter

Aprender

Pruebe, compre y venda

Comunidades

Acerca de la documentación de Red Hat

Ayudamos a los usuarios de Red Hat a innovar y alcanzar sus objetivos con nuestros productos y servicios con contenido en el que pueden confiar. Explore nuestras recientes actualizaciones.

Hacer que el código abierto sea más inclusivo

Red Hat se compromete a reemplazar el lenguaje problemático en nuestro código, documentación y propiedades web. Para más detalles, consulte el Blog de Red Hat.

Acerca de Red Hat

Ofrecemos soluciones reforzadas que facilitan a las empresas trabajar en plataformas y entornos, desde el centro de datos central hasta el perímetro de la red.

Theme

© 2025 Red Hat