jsfjsf-2flash-scope

Why is flash.keep() removing the key if it is in a bean but not in EL in Facelets page?


So I have this very small JSF example:

<!DOCTYPE html>
<html xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
    <title>Index</title>
</h:head>
<h:body>
    <h:form>
        <h:commandButton action="#{reqScopedBackingBean.foo()}" value="Submit"/>
    </h:form>
</h:body>
</html>

and the foo is implement as seen below:

package biz.tugay.jsftags;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;

@ManagedBean
@RequestScoped
public class ReqScopedBackingBean {
    public String foo() {
        FacesContext.getCurrentInstance().getExternalContext().getFlash().put("message", "Success!!");
        return "hello?faces-redirect=true";
    }
}

and finally hello.xhtml is as follows:

<!DOCTYPE html>
<html xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
    <title>Hello</title>
</h:head>
<h:body>
    <h:outputText value="#{flash.keep.message}"/>
</h:body>
</html>

in the above example, when I hit Submit, I will be redirected to hello.xhtml and see the "Success!!" text just fine. And when I refresh the page, I will still see the message because I am calling the keep method as seen below:

#{flash.keep.message}

Now moving on to another example:

index.xhtml and ReqScopedBackingBean are same, however this time hello.xhtml is as follows:

<!DOCTYPE html>
<html xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
    <title>Hello</title>
</h:head>
<h:body>
    #{otherBean.pullValuesFromFlash()}
    <h:outputText value="#{otherBean.message}"/>
</h:body>
</html>

and OtherBean.java is as follows:

@ManagedBean
@RequestScoped
public class OtherBean {

    private String message;

    public void pullValuesFromFlash() {
        final Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
        flash.keep("message");
        final String message = (String) flash.get("message");
        setMessage(message);
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

I am expecting the behavior to be same with the first example. However, when I am on hello.xhtml, I will not see the success message. However, if I comment out the line:

// flash.keep("message");

then the message will appear(but refresh on hello.xhtml will not re-show the Success message as in the first example).

My question is, what is the difference here? How is

#{flash.keep.message}

and different than

flash.keep("message");
final String message = (String) flash.get("message");

?


Solution

  • You've hit a Mojarra bug of Flash#keep(). Basically, Mojarra unnecessarily removes the entry from the current flash scope before putting it in the next flash scope.

    It'll work if you reorder the logic as below.

    public void pullValuesFromFlash() {
        Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
        String message = (String) flash.get("message");
        flash.keep("message");
        setMessage(message);
    }
    

    I have fixed this bug as per issue 4167.


    Unrelated to the concrete problem, those <h:outputText>s are unnecessary. You can safely remove them to reduce boilerplate. See also Is it suggested to use h:outputText for everything?