JSF request scoped bean keeps recreating new Stateful session beans on every request?
Asked Answered
S

2

32

I'm building my first Java EE application using JSF, PrimeFaces, Glassfish and Netbeans. Because I'm new, it's possible I'm approaching the core problem wrong.

Core problem: I want to maintain user's information securely. There seems to be conflicting ideas on whether it should be maintained in a JSF session bean or a stateful session EJB. I'm trying to use a stateful session EJB because it is more secure that way.

The problem is that my application seems to be creating multiple instances of that bean when I expect it to create one and re-use it. If I refresh the page it runs the @PostConstruct and @PostActivate 3 times, all of them with a different instances. Then they all get destroyed when I re-deploy the application.

Did I misunderstand how it should work or is something wrongly configured?

I'll try to show a trimmed down code sample:

basic.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"
      xmlns:c="http://java.sun.com/jsp/jstl/core">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        Hello from Facelets
        <c:if test="#{loginController.authenticated}">
            Authenticated
        </c:if>
        <c:if test="#{loginController.authenticated}">
            Authenticated
        </c:if>
        <c:if test="#{loginController.authenticated}">
            Authenticated
        </c:if>
    </h:body>
</html>

LoginController:

@Named(value = "loginController")
@RequestScoped
public class LoginController implements Serializable {

    @EJB
    private UserBeanLocal userBean;

    public boolean isAuthenticated() {
        return userBean.isAuthenticated();
    }

}

UserBean (excluding UserBeanLocal interface)

@Stateful
public class UserBean implements UserBeanLocal, Serializable {

    boolean authenticated = false;

    @PostConstruct
    @PostActivate
    public void setup(){
        System.out.println("##### Create user Bean: "+this.toString());
    }

    @Override
    public boolean isAuthenticated() {
        System.out.println("########## Authentication test is automatically passing.");
        authenticated = true;//hard coded for simplicity.
        return authenticated;
    }     

    @PrePassivate
    @PreDestroy
    public void cleanup(){
        System.out.println("##### Destroy user Bean");
    }

}

Finally, here is the Glassfish output after refreshing three times:

INFO: ##### Create user Bean: boundary._UserBean_Serializable@2e644784
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ##### Create user Bean: boundary._UserBean_Serializable@691ae9e7
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ##### Create user Bean: boundary._UserBean_Serializable@391115ac
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
Sulphuric answered 16/1, 2012 at 22:24 Comment(2)
Although this is old (and well-answered) I thought it worth mentioning also that the reason 'they all get destroyed when [you] re-deploy the application' is because when you inject a stateful session bean using @EJB it's up to the application to trigger the @PreDestroy VIA a @Remove method, otherwise it just hangs around. That's why your output never hits cleanup(). See also this demo/test app for some @EJB vs CDI @Inject lifecycle cases.Memoir
Sorry: typo test app here .Memoir
S
62

Stateful session beans (SFSB) are not exactly what you think they are. You seem to think that they behave somehow like session scoped JSF managed beans. This is untrue. The term "session" in EJBs has an entirely different meaning than the HTTP session which you've had in mind.

The "session" in EJBs must be interpreted in transactional context. The transaction (basically, the DB session) lives in case of SFSB as long as the client lives. The SFSB's client is in your particular example not the webbrowser, but the JSF managed bean instance itself, exactly the one where the SFSB is been injected. Since you have put the JSF managed bean in the request scope, the SFSB will be recreated on every HTTP request together with the JSF managed bean.

As an example, try to put the JSF managed bean in the view scope. The view scope is useful for a multi-step form on the same page, for example. Everytime when the view postbacks to itself, then the same JSF managed bean instance will be reused and this instance gives you access to the same instance of the SFSB as it is when the bean was created, which is not shared elsewhere. The SFSB transaction lives as long as the client (the view scoped JSF managed bean) lives.

A stateless session bean (SLSB) can be shared elsewhere, but that shouldn't matter as it's intented to be treated as stateless anyway. This "feature" saves the container time and memory to create and store them. The container can just have a pool of them. Even more, the SLSB instance which is been injected in a view, session or application scoped JSF managed bean does not necessarily need to refer exactly the same instance on every HTTP request as it was during JSF managed bean's creation. It can even be a completely different instance, depending on the available instances in the container's pool. The transaction lives (by default) as long as a single method call on the SLSB.

That said, a SFSB is unsuitable for your particular case of "remembering a logged-in user". That it's "more secure" makes really no sense. Just put the JSF managed bean in the session scope and let it remember the logged-in user by itself and make use of a SLSB to do any business actions (such as interacting with the DB) and use SFSB only when you want a real stateful session bean (I assume that you now understand what exactly they are :) ).

See also:

Silage answered 17/1, 2012 at 4:23 Comment(5)
Thank you, that makes much better sense. Here are the articles I was reading as to why I was trying to do it this way. oio.de/public/java/… datadisk.co.uk/html_docs/ejb/ejb3_session_beans.htmSulphuric
The second one is an excellent article. I now see where you're coming from with "more secure". I think that you now realize well that it should be interpreted in transactional context, not in webapp context. The first one makes good points, but the overall explanation and examples are weak. I'd suggest this article as a starting point instead.Silage
Wonderful, I love a good read. Thanks again for the additional feedback.Sulphuric
what if the SFSB IS the managed bean ? (annotated with both Named and Stateful). I would expect that it keeps state through requests (ie. like @SessionScoped) but it looks like it's not the case.Kephart
@gpilotino: Annotate it with CDI @SessionScoped. I'd only not suggest this tight-coupling.Silage
O
2

As far as I understand from my investigation and usage, EJB SFSB is not usefull for web applications since JSF, Spring provides helfull annotation to keep the session per user. But in case where webservice and RPC method invocation call required application is running, EJB SFSB is neccs to keep the session(trancation) per user.

Odaodab answered 16/7, 2013 at 8:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.