@SessionScoped bean injected as @ManagedProperty of a @ViewScoped acts like @RequestScoped in MyFaces, works fine in Mojarra

StackOverflow https://stackoverflow.com/questions/18425247

  •  26-06-2022
  •  | 
  •  

Question

Here is my simple example:

Index.xhtml in root:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:inputText value="#{index.variable}"></h:inputText>
            <h:commandButton action="#{index.submit()}" type="submit"></h:commandButton>
        </h:form>
    </h:body>
</html>

Its ManagedBean:

import java.io.IOException;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

@ManagedBean
@ViewScoped
public class Index implements Serializable {

    @ManagedProperty("#{sessionBean}")
    private SessionBean sessionBean; /*getter&setter*/
    private String variable; /*getter&setter*/

    public void submit() {
        sessionBean.setAsd(variable);
        ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
        try {
            context.redirect("next");
        } catch (IOException ex) {
        }
    }
}

/next/index.xhtml:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Check variable</title>
    </h:head>
    <h:body>
        #{sessionBean.asd}
    </h:body>
</html>

SessionBean.java:

import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class SessionBean implements Serializable {

    private String asd;

    public String getAsd() {
        return asd;
    }

    public void setAsd(String asd) {
        this.asd = asd;
    }
}

If I use mojarra implementation everything works as expected: after form submitting, user gets redirected to root/ and see the value that was printed in the form of index.xhtml.

But if I use myfaces, asd becomes null right after existing form gets submitted. SessionScoped bean acts like RequestScoped

Why?


here is my web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <listener>
        <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
        <!--listener-class>com.sun.faces.config.ConfigureListener</listener-class-->
    </listener>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <welcome-file-list>
        <welcome-file>index.xhtml</welcome-file>
    </welcome-file-list>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

enter image description here

Server: Apache Tomcat 7.0.34

UPDATE: it works if we change the ViewScoped annotation of Index.java bean to RequestScoped or SessionScoped. But why?

Était-ce utile?

La solution

I currently have a JSF 2.1 project written with the help of Mojarra. Just for experimenting, I changed the implementation to MyFaces and ran the application only to see issues similar to yours (all my injected @ManagedProperty variables end up null after a POST submit). I switched back to Mojarra and the app runs fine. So, basically something is different in the MyFaces implementation.

A bit of googling led me to this unresolved issue - MYFACES-3656. Here's an excerpt from the issue reporter on how to fix the issue:

If you set the org.apache.myfaces.SERIALIZE_STATE_IN_SESSION to false and redeploy the application then everything works as expected.

How does this help? The answer is in the comments section:

I think the behavior described is expected (different to say that the behavior described is desired or intentionally done in that way).

What's happening here is in MyFaces serialization is set to true by default (some old lines from JSF 1.0 spec says so, even if RI does not implement it in this way). In JSF 2.2 spec, SERIALIZE_STATE_IN_SESSION param will be standardized and set to false by default.

Serialization causes that all beans under view scope are in fact "recreated". If the param is set to false, the beans are stored into session and on further requests are used, looking like everything is ok, but that fact is not true because in a cluster configuration the same application will fail.

Only the first time the view scope bean is created, the references from managed-property takes effect, but if the bean is serialized/deserialized, the references are not restored back, because on the serialization step, even the application and session scope beans are serialized too.

.....

How to solve it? I haven't found a decent solution to this issue. One could think on just restore the view scope bean and reapply @ManagedProperty annotations or entries found in faces-config.xml, but the problem is the view scope bean still is storing information that shouldn't be there from start (only marking the fields as transient will do the trick). It is possible define an special mode were this hack or some variant is done, but it will be only in myfaces and it cannot be enabled by default.

Similar issue has been reported and replied with the above explanation in this mailing-list archive

Now, since in your case you are not explicitly setting a STATE_SAVING_METHOD, it defaults to server. Consequently, SERIALIZE_STATE_IN_SESSION comes into effect and defaults to true.

I tried your code with MyFaces on Tomcat and set SERIALIZE_STATE_IN_SESSION to false and it works. However, in the event, you set the STATE_SAVING_METHOD to client, nothing will work and you'll get a view state not found error.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top