When does JSF creates a session & what does it puts in a session map?

时间:2016-04-15 15:11:49

标签: jsf-2 httpsession mojarra

I am running Mojarra 2.2.0.

  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>

The managed bean action method is-

public void action() {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance()
                .getExternalContext().getSession(false);
        System.out.println(session.getId()); // not null for stateful views
    }

For stateless views session.getId() throws NPE

For views which are not stateless- Firing a GET request, there is JSESSIONID=340041C96D5AA446D761C3602F54A76D

I read it here that-

For client side state saving mechanism, JSF won't create the session and will store the view state in a hidden input field with the name javax.faces.ViewState in the form whenever necessary.

Further, it's mentioned here that

JSF will indeed autocreate the session because the JSF view state has to be stored over there. If you set the JSF state saving method to client instead of server, then it won't be stored in session and hence no session needs to be created

I think the above line is a source for trouble for me.

If you set the JSF state saving method to client instead of server, then it won't be stored in session // FULLY AGREED

and

hence no session needs to be created. // This confuses because for client side saving mechanism, a session id gets generated by the servlet container & hence there is a session associated with the request.

In reference to the discussion which I had with BalusC in this question, I created a HttpSessionListener-

@WebListener
public class MyHttpSessionListener implements HttpSessionListener {

    public void sessionCreated(HttpSessionEvent event) {
        Thread.dumpStack();
    }

    public void sessionDestroyed(HttpSessionEvent event) {

    }

}

See below attached screenshots(these 2 screenshots are for version 2.0.3, there must have been an old bug due to which the session was getting created)-

enter image description here

enter image description here

Libraby (Mojarra 2.2.0)- enter image description here

1 个答案:

答案 0 :(得分:4)

  

JSF何时创建会话

最简单的naildown方法是创建HttpSessionListener,在sessionCreated()方法上放置一个调试断点,并检查第一次需要获取会话的调用堆栈(因此隐式需要创建它)。

在下面的示例中,您将在调用堆栈中看到一串getSession()个调用。您将看到FaceletViewHandlingStrategy.renderView()方法是第一次调用它的方法。

enter image description here

在调试器的调用堆栈中单击FaceletViewHandlingStrategy.renderView()行后,您将进入其源代码(Maven将自动加载源代码,否则您需要手动附加它)。

enter image description here

你看,当启用服务器端状态保存并且渲染视图不是暂时的(stateless)时,JSF将隐式创建会话,以确保按时创建它保存视图(如果会话是在以后创建的,例如在渲染响应阶段,您可能会冒这样的异常风险Adding <h:form> causes java.lang.IllegalStateException: Cannot create a session after the response has been committed)。

您在源代码中也会立即看到当状态保存方法设置为客户端时,或者视图是<f:view transient="true">中的无状态时,JSF不再隐式创建会议。较旧的JSF版本可能会如您所知那样做,但这应该被视为一个错误,应该在较新版本中修复。

如果您希望确保无状态并避免意外/无法预见的会话创建,那么您可以在throw new IllegalStateException()方法内sessionCreated()。当发生这种情况时,您只需要查看负责创建会话的调用堆栈,然后修改/更改代码,使其不再执行此操作。

  

它在会话地​​图中放了什么?

在幕后,ExternalContext#getSessionMap()代表HttpSession#setAttribute() / getAttribute() / removeAttribute()。您可以使用HttpSessionAttributeListener来听取这些方法。

在下面的示例中,您会看到ViewScopeContextManager.getContextMap()行调用SessionMap#put()方法,以便在会话地图中添加内容。当您展开event参数时,您将看到会话属性名称和值,分别为com.sun.faces.application.view.activeViewContexts和空ConcurrentHashMap

enter image description here

实际上,我使用的是@Named @ViewScoped,它被特定页面上的值表达式引用(您在调用堆栈中看到EL解析器和Weld解析器更下方)。当您单击调用堆栈中的ViewScopeContextManager.getContextMap()行时,您将看到它只是在会话范围内准备一个映射,以便存储视图范围的bean。

enter image description here

这只是一个例子。会话中可以存储更多内容。以这种方式使用调试器并检查相关的源代码将对 Why 有很多了解。