Mentawai offers stateful actions to persist state across multiple requests from the same client without requiring you to use the old and akward http session object. Let's see an example.
Take the classical Number Guess game, where you try to guess a number between 0 and 100 while the computer tells you if your guess is higher or lower than the correct answer. There can be a lot of state that needs to be maintained like:
The standard solution to this problem is to place everything inside the user session (HttpSession), but Mentawai offers you a more natural solution: Stateful or Sticky Actions.
Sticky Actions will persist its state across multiple invocations from the time you call adhere() until you release the action by calling disjoin(), in other words, the same action instance will be used for consecutive action requests. You can also trap the onRemoved() callback for when the session expires or gets invalidated before the disjoin() method is called. (For example when the user leaves the page or closes the browser).
Below is an example of how you could implement the Number Guess game using the session. Then we show an alternative implementation using sticky actions:
Implementing the Number Guess game using the session:
public class NumberGuess extends BaseAction { public static final String BINGO = "bingo"; public static final String FIRST = "first"; public static final String WRONG = "wrong"; private static final Random random = new Random(); private void incrementGuesses() { Integer i = (Integer) session.getAttribute("guesses"); session.setAttribute("guesses", new Integer(i.intValue() + 1)); } public String execute() { // Check if a game is in progess, by fetching the current answer... int answer = -1; if (session.getAttribute("answer") == null) { answer = random.nextInt(101); session.setAttribute("answer", new Integer(answer)); session.setAttribute("guesses", new Integer(0)); } else { answer = ( (Integer) session.getAttribute("answer")).intValue(); } if (input.getValue("guess") != null) { incrementGuesses(); int guess = input.getIntValue("guess"); if (answer == guess) { output.setValue("answer", session.getAttribute("answer").toString()); output.setValue("guesses", session.getAttribute("guesses").toString()); session.removeAttribute("answer"); session.removeAttribute("guesses"); return BINGO; } else { if (guess < answer) { addMessage("Number is higher!"); // of course this can be i18n... } else { addMessage("Number is lower!"); // of course this can be i18n... } return WRONG; } } return FIRST; } }
Implementing the Number Guess games using a stateful action:
public class NumberGuessSticky extends BaseAction { public static final String BINGO = "bingo"; public static final String FIRST = "first"; public static final String WRONG = "wrong"; // because this action is sticky, you can keep your values here across requests... private int answer = -1; private int guesses = 0; private static final Random random = new Random(); public String execute() { System.out.println("Executing action: " + this.toString()); // Check if a game is in progess, by fetching the current answer... if (answer == -1) { answer = random.nextInt(101); guesses = 0; adhere(); } if (input.getValue("guess") != null) { guesses++; output.setValue("guesses", String.valueOf(guesses)); int guess = input.getIntValue("guess"); if (answer == guess) { output.setValue("answer", String.valueOf(answer)); disjoin(); return BINGO; } else { if (guess < answer) { addMessage("Number is higher!"); // of course this can be i18n... } else { addMessage("Number is lower!"); // of course this can be i18n... } return WRONG; } } return FIRST; } }