jsfhttp-redirectjsf-2messageflash-scope

flash.keep & flash.setKeepMessages(true) on redirection


I am going through an example in Anghel Leonard book Mastering Java Server Faces 2.2.

The author demonstrates with an example on how preserve data for the next request on redirection when the bean is made is @RequestScoped.

enter image description here enter image description here

This is the code for index.xhtml-

<h:body>
    <f:metadata> 
        <f:event type="preRenderView" 
            listener="#{playersBean.pullValuesFromFlashAction}"/> 
    </f:metadata>
    <h:messages />  
    <h:form>                       
        Name: <h:inputText value="#{playersBean.playerName}"/>
        Surname: <h:inputText value="#{playersBean.playerSurname}"/>
        <h:commandButton value="Register" 
            action="#{playersBean.addValuesToFlashAction()}"/>          
    </h:form>
</h:body>

terms.xhtml-

<h:body>
    <h:messages />  
    Hello, <h:outputText value="#{flash.keep.playerName} #{flash.keep.playerSurname}"/>    
    <br/><br/>Terms &amp; Conditions ... ... ... ... ...
    <h:form>
        <h:commandButton value="Reject" 
            action="#{playersBean.termsRejectedAction()}" />
        <h:commandButton value="Accept" 
            action="#{playersBean.termsAcceptedAction()}" />
    </h:form>
</h:body>

done.xhtml-

<h:head>
    <title></title>
</h:head>   
<h:body>
    <f:metadata> 
        <f:event type="preRenderView" 
            listener="#{playersBean.pullValuesFromFlashAction}"/> 
    </f:metadata>
    <h:messages />  
    <h:outputText value="#{playersBean.playerName} #{playersBean.playerSurname}"/> 
        successfully registered!           
</h:body>

And the backing bean-

@ManagedBean
@RequestScoped
public class PlayersBean {

    private final static Logger logger = Logger.getLogger(PlayersBean.class.getName());
    private String playerName;
    private String playerSurname;
    public PlayersBean() {
    }
    public String getPlayerName() {
        return playerName;
    }
    public void setPlayerName(String playerName) {
        this.playerName = playerName;
    }
    public String getPlayerSurname() {
        return playerSurname;
    }
    public void setPlayerSurname(String playerSurname) {
        this.playerSurname = playerSurname;
    }
    public String addValuesToFlashAction() {

        Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
        flash.put("playerName", playerName);
        flash.put("playerSurname", playerSurname);
        return "terms?faces-redirect=true";
    }
    public void pullValuesFromFlashAction(ComponentSystemEvent e) {
        Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
        playerName = (String) flash.get("playerName");
        playerSurname = (String) flash.get("playerSurname");
    }
    public String termsAcceptedAction() {
        Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();

        flash.setKeepMessages(true);
        pullValuesFromFlashAction(null);

        //do something with firstName, lastName 
        logger.log(Level.INFO, "First name: {0}", playerName);
        logger.log(Level.INFO, "Last name: {0}", playerSurname);

        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Terms accepted and player registered!"));
        return "done?faces-redirect=true";
    }
    public String termsRejectedAction() {
        Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();

        flash.setKeepMessages(true);
        pullValuesFromFlashAction(null);

        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Terms rejected! Player not registered!"));
        return "index?faces-redirect=true";
    }
}

I think there was no need for these 2 lines-

flash.setKeepMessages(true);
pullValuesFromFlashAction(null);

as flash.keep on terms.xhtml page serves the same purpose. The author seems to be confused here.

Or am I completely wrong & they do definitely serves a purpose over here.


Solution

  • The code chosen by the author is a bit confusing, from my point of view, however, it does what it is intended to do.

    flash.setKeepMessages(true);

    This line tells JSF you want to keep the FacesMessage in the flash scope. That's a requirement when making a redirection, because more than one subsequent requests are involved and the messages would die from one request to another otherwise. The line is necessary.

    pullValuesFromFlashAction(null);

    The line does nothing special, just grabs the name and the surname from the flash scope. It's just right to call it from preRenderView to load bean data from the flash scope, but it's redundant to invoke it from the action methods (unless you want to do some logging to see parameters are properly set). Anyway, if you're starting with JSF I encourage you to use JSF 2.2 which has a parameterless replacement for the preRenderView methods, called viewAction.

    Last but not least, I would recommend you going through an answer I made some time ago showing an example for dealing with the flash scope, you might find it interesting.

    See also: