There are a few basic things to keep in mind when working with stateful session beans:
1. When a client obtains a reference to a stateful session bean, a private instance of that bean is created for the client. In other words, there is one bean instance per client reference.
2. The bean does not go away until the client invokes a method annotated with @Remove. If the client forgets or is unable to end the conversation with the bean, it will hang around until the server can determine that it is safe to remove it.
3. A reference to a stateful session bean cannot be shared between threads.
A consequence of these rules is that clients need to plan carefully on when they need to start the session and when it can be ended. It also means that using the @EJB annotation to inject a stateful session bean is not a good solution. Servlets, stateless session beans, and message-driven beans are all stateless components. As we stated before in the description of stateless session beans, that means that any state placed on a stateless component must also be stateless as well. A stateful session bean reference is itself stateful because it references a private instance of the bean managed by the server. If @EJB were used to inject a stateful session bean into a stateless session bean where the server had pooled 100 bean instances, then there would be 100 stateful session bean instances created as well. The only time it is ever safe to inject a stateful session bean is into another stateful session bean.
Dependency lookup is the preferred method for acquiring a stateful session bean instance for a stateless client. The EJBContext lookup() method is the easiest way to accomplish this, but JNDI will be required if the client is a servlet. Listing 3-23 demonstrates a typical pattern for servlets using stateful session beans. A reference is declared to the bean, it is looked up lazily when needed, and the result is bound to the HTTP session. The stateful session bean and HTTP session have similar life cycles, making them good candidates to work together.
Listing 3-23: Creating and Using a Stateful Session Bean
@EJB(name="cart", beanInterface=ShoppingCart.class)
public class ShoppingCartServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
ShoppingCart cart = (ShoppingCart) session.getAttribute("cart");
if (cart == null) {
try {
Context ctx = new InitialContext();
cart = (ShoppingCart) ctx.lookup("java:comp/env/cart");
session.setAttribute("cart", cart);
} catch (NamingException e) {
throw new ServletException(e);
}
}
if (request.getParameter("action").equals("add")) {
String itemId = request.getParameter("item");
String quantity = request.getParameter("quantity");
cart.addItem(itemId, Integer.parseInt(quantity));
}
if (request.getParameter("action").equals("cancel")) {
cart.cancel();
session.removeAttribute("cart");
}
// ...
}
}
When the server receives a request to look up a stateful session bean, it asks the EJB container to create a new instance of the bean, which is then assigned a unique identifier. The reference to the bean that is returned keeps track of this identifier and uses it when communicating with the server to ensure that the right bean instance is used to invoke each business method.