98
votes

I am going through the Java EE 6 tutorial and I am trying to understand the difference between stateless and stateful session beans. If stateless session beans do not retain their state in between method calls, why is my program acting the way it is?

package mybeans;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;

@LocalBean
@Stateless
public class MyBean {

    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void increment() {
        this.number++;
    }
}

The client

import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
import mybeans.MyBean;
import java.io.PrintWriter;

@WebServlet(name = "ServletClient", urlPatterns = { "/ServletClient" })
public class ServletClient extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @EJB
    MyBean mybean;

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        PrintWriter out = response.getWriter();
        mybean.increment();
        out.println(mybean.getNumber());
    }

}

I was expecting getNumber to return 0 every time but it is returning 1 and reloads of the servlet in my browser increase it more. The problem is with my understanding of how stateless session beans work and not with the libraries or application server, of course. Can somebody give me a simple hello world type example of a stateless session bean that behaves differently when you change it to stateful?

7
Related: stackoverflow.com/questions/8887140/… This answer is maybe simpler to understand. Note that servlets are basically application scoped (there's only 1 servlet instance applicationwide which is shared/reused across all HTTP requests/sessions.BalusC
hi, You do first increment, and then get the value .... so You cannot expect value of 0.rzur2004
I just wanna thank you for asking this, it addresses my problem at the moment. I could not have asked that betterkholofelo Maloma

7 Answers

138
votes

Stateless Session Beans (SLSB) are not tied to one client and there is no guarantee for one client to get the same instance with each method invocation (some containers may create and destroy beans with each method invocation session, this is an implementation-specific decision, but instances are typically pooled - and I don't mention clustered environments). In other words, although stateless beans may have instance variables, these fields are not specific to one client, so don't rely on them between remote calls.

In contrast, Stateful Session Beans (SFSB) are dedicated to one client for their entire life, there is no swapping or pooling of instances (it may be evicted from memory after passivation to save resources but that's another story) and maintain conversational state. This means that the instance variables of the bean can keep data relative to the client between method invocations. And this makes possible to have interdependent method calls (changes made by one method affect subsequent method calls). Multi-step processes (a registration process, a shopping cart, a booking process...) are typical use cases for SFSB.

One more thing. If you are using SFSB, then you must avoid injecting them into classes that are multithreaded in nature, such as Servlets and JSF managed beans (you don't want it to be shared by all clients). If you want to use SFSB in your web application, then you need to perform a JNDI lookup and store the returned EJB instance in the HttpSession object for future activity. Something like that:

try {
    InitialContext ctx = new InitialContext();
    myStateful = (MyStateful)ctx.lookup("java:comp/env/MyStatefulBean");
    session.setAttribute("my_stateful", myStateful);
} catch (Exception e) {
    // exception handling
}
97
votes

The important difference is not private member variables, but associating state with a particular user (think "shopping cart").

The stateful piece of stateful session bean is like the session in servlets. Stateful session beans allow your app to still have that session even if there isn't a web client. When the app server fetches a stateless session bean out of the object pool, it knows that it can be used to satisfy ANY request, because it's not associated with a particular user.

A stateful session bean has to be doled out to the user that got it in the first place, because their shopping cart info should be known only to them. The app server ensures that this is so. Imagine how popular your app would be if you could start shopping and then the app server gave your stateful session bean to me when I came along!

So your private data member is indeed "state", but it's not "shopping cart". Try to redo your (very good) example to make it so the incremented variable is associated with a particular user. Increment it, create a new user, and see if they can still see the incremented value. If done correctly, every user should see just their version of the counter.

18
votes

Stateless and stateful in this context don't mean quite what you might expect.

Statefulness with EJBs refers to what I call conversational state. The classic example is a flight booking. If it consists of three steps:

  • Reserve seat
  • Charge credit card
  • Issue Ticket

Imagine each of those is a method call to a session bean. A stateful session bean can maintain this kind of conversation so it remembers what happens between calls.

Stateless session beans don't have such capacity for conversational state.

Global variables inside a session bean (stateless or stateful) are something else entirely. Stateful session beans will have a pool of beans created (since a bean can only be used in one conversation at a time) whereas stateless sesion beans will often only have one instance, which will make the global variable works, but I don't think this is necessarily guaranteed.

7
votes

Good Question,

try this code (change MyBean Stateful/Stateless.):

import javax.ejb.LocalBean;
import javax.ejb.Stateful;
import javax.ejb.Stateless;

@LocalBean 
@Stateless 
public class MyBean {

    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void increment() {
        this.number++;
    }
}

Servlet_1

 import java.io.IOException;
    import javax.ejb.EJB;
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.WebServlet;

    import java.io.PrintWriter;

    @WebServlet(name = "ServletClient", urlPatterns = { "/ServletClient" })
    public class ServletClient extends HttpServlet {

        private static final long serialVersionUID = 1L;

        @EJB
        MyBean mybean;

        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

            PrintWriter out = response.getWriter();
            mybean.increment();
            out.println(mybean.getNumber());
        }

    }

Servlet_2

import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;

import java.io.PrintWriter;

@WebServlet(name = "NewServletClient", urlPatterns = { "/NewServletClient" })
public class NewServletClient extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @EJB
    MyBean mybean;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        PrintWriter out = response.getWriter();
        mybean.increment();
        out.println(mybean.getNumber());
    }

}

case : MyBean -@Stateless

http://localhost:8080/MYServletDemo/ServletClient

1

http://localhost:8080/MYServletDemo/ServletClient

2

http://localhost:8080/MYServletDemo_war_exploded/newServletClient

3

http://localhost:8080/MYServletDemo/ServletClient

4

case : MyBean -@Stateful

http://localhost:8080/MYServletDemo/ServletClient

1

http://localhost:8080/MYServletDemo/ServletClient

2

http://localhost:8080/MYServletDemo/newServletClient

1

http://localhost:8080/MYServletDemo/ServletClient

3

5
votes

The major differences between the two major types of session beans are:

Stateless Beans

  1. Stateless Session Beans are the ones which have no conversational state with the client which has called its methods. For this reason they can create a pool of objects which can be used to interact with multiple clients.
  2. Performance wise stateless beans are better since they don't have states per client.
  3. They can handle multiple requests from multiple clients in parallel.

Stateful Beans

  1. Stateful session beans can maintain the conversational state with multiple clients at a time and the task is not shared between the clients.
  2. After the session is completed the state is not retained.
  3. The container can serialize and store the state as a stale state for future use. This is done to save resources of the application server and to support bean failures.
4
votes

This thing happen because the container only has one bean instance in the pool that is being reused for all calls. If you run the clients in parallel you will see a different result because the container will create more bean instances in the pool.

4
votes

It has good answers. I would like to add small answer. Stateless Bean should not used to hold any client data. It should be used to "to model actions or processes that can be done in one shot".