@Stateful vs. @SessionScoped

CDI's @SessionScoped annotation specifies CDI beans maintained within a HTTP session:

"...The session context is shared between all servlet requests that occur in the same HTTP session. The session context is destroyed when the HTTPSession times out, after all HttpSessionListener's have been called, and at the very end of any request in which invalidate() was called, after all filters and ServletRequestListener s have been called..."
JSR 346: Contexts and Dependency Injection for JavaTM EE 1.1,[6.7.2 Session context lifecycle]

The EJB @Stateful annotation denotes classes which are entirely controlled by the client. Their lifecycle is completely independent from HTTP session. In fact EJBs could be even present in environments without an installed servlet container.

An @Stateful EJB instance is exclusively created for a client when:

"...A session bean instance’s life starts when a client obtains a reference to a stateful session bean instance through dependency injection or JNDI lookup, or when the client invokes a create METHOD method on the session bean’s home interface..."
The same instance is destroyed in case:
"...When the client calls a business method of the bean that has been designated as a Remove method on the bean class or a remove method on the home or component interface, the container invokes PreDestroy lifecycle callback interceptor methods, if any, for the bean instance after the Remove method completes..."
JSR 345: Enterprise JavaBeansTM 3.2, [4.6 Stateful Session Beans]

Although the names are similar, @Stateful and @SessionScoped behave differently. @SessionScoped bean instances are maintained within the HTTP session. Only one instance of a given class can exist within a HTTP session. All @SessionScoped instances are destroyed together with the HTTP session.

@Stateful EJBs, on the other hand, maintain usually 1:1 relation between an instance and the client. The client is completely in charge of creating and destroying the instances. A single client (=usual Java class) could even maintain multiple @Stateful instances.

@Stateful EJBs can be also denoted with @SessionScoped annotation. Then the active HTTP session becomes the "EJB client" and maintains the instances.

See you at Java EE Workshops at Munich Airport, Terminal 2 or Virtual Dedicated Workshops / consulting. Is Munich's airport too far? Learn from home: airhacks.io.

Comments:

Nice explanation! Thanks.
Now the difference is clear to me.

Posted by Csaba on June 13, 2016 at 10:26 AM CEST #

Thank you! Now I understand :)

Posted by Eduard on October 13, 2016 at 09:18 PM CEST #

It seems not to be as simple. This doesn't work as expected because the bean is only instantiated once per servlet instance:

@Stateful
@SessionScoped
public class CounterBean {
private int count = 0;

public int getCount() {
return ++this.count;
}
}

@WebServlet(urlPatterns = {"/index.html"})
public class IndexServlet extends HttpServlet {
@EJB
CounterBean counterBean;

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.println("Session ID: " + request.getSession().getId());
out.println("Counter: " + counterBean.getCount());

out.flush();
}
}

This instead works, but doesn't use @SessionScoped at all:

@Stateful
public class CounterBean {
private int count = 0;

public int getCount() {
return ++this.count;
}
}

@WebServlet(urlPatterns = {"/index.html"})
public class IndexServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

// Create a new bean instance and store it in the session
HttpSession session = request.getSession();
CounterBean counterBean = (CounterBean) session.getAttribute("counterBean");

if (counterBean == null) {
try {
InitialContext ctx = new InitialContext();
counterBean = (CounterBean) ctx.lookup("java:global/SessionScoped_EJB/CounterBean");
} catch (NamingException ex) {
throw new ServletException(ex);
}

session.setAttribute("counterBean", counterBean);
}

// Call the bean
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.println("Session ID: " + session.getId());
out.println("Counter: " + counterBean.getCount());

out.flush();
}
}

What am I missing? :-)

Kind regards, Dennis

Posted by Dennis Schulmeister-Zimolong on February 11, 2018 at 05:57 PM CET #

Regarding your remark on SessionScoped EJBs: Does this also apply to @RequestScoped? And for which application servers have you observed this? Is this demanded by the specification?

Under WildFly, with a RequestScoped EJB, I've noticed some EJB heap pollution, leading me to believe that my EJBs are actually not removed with the CDI destroy on leaving the request scope.

Posted by Jan-Willem Gmelig Meyling on August 27, 2018 at 03:31 PM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
...the last 150 posts
...the last 10 comments
License