jakarta-eeapache-tomeestateful-session-bean

How to make data persistent across pages using a Stateful Session Bean?


I'm working on a shopping cart with JEE. My web application is made of one stateful session bean and two servlets with corresponding JSP pages. One servlet allows to add new items to the cart, the other allows to list the content of the cart.

The stateful session bean is supposed to save the state of the cart across pages, but the cart always appears to be empty. Could you help me to find out what's wrong with my code and explain why the current implementation is not sufficient ?

Servlet to add a new item to the cart:

package my.servlet;

import my.entity.BookEntity;
import my.entity.ItemEntity;
import my.session.BookBean;
import my.session.CartBean;

import javax.ejb.EJB;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/item")
public class ItemServlet extends HttpServlet {

  @EJB
  private BookBean bookBean;

  @EJB
  private CartBean cartBean;

  @PersistenceContext(unitName = "book-pu")
  private EntityManager bookManager;

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
  {
    ServletContext ctx = getServletContext();
    long book = Long.parseLong(request.getParameter("book"), 10);
    RequestDispatcher dispatcher = ctx.getRequestDispatcher("/jsp/item.jsp");
    request.setAttribute("book", bookManager.find(BookEntity.class, book));
    request.setAttribute("item", null);
    dispatcher.forward(request, response);
  }

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
  {
    ServletContext ctx = getServletContext();
    int qty = Integer.parseInt(request.getParameter("qty"), 10);
    long book = Long.parseLong(request.getParameter("book"), 10);
    RequestDispatcher dispatcher = ctx.getRequestDispatcher("/jsp/item.jsp");
    ItemEntity item = new ItemEntity(bookManager.find(BookEntity.class, book), qty);
    request.setAttribute("book", bookManager.find(BookEntity.class, book));
    request.setAttribute("item", item);
    cartBean.addItem(item);
    dispatcher.forward(request, response);
  }
}

Servlet to list the content of the cart:

package my.servlet;

import my.session.CartBean;

import javax.ejb.EJB;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/cart")
public class CartServlet extends HttpServlet {

  @EJB
  private CartBean cartBean;

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
  {
    ServletContext ctx = getServletContext();
    RequestDispatcher dispatcher = ctx.getRequestDispatcher("/jsp/cart.jsp");
    request.setAttribute("items", cartBean.getAllItems());
    dispatcher.forward(request, response);
  }
}

The Stateful Session Bean:

package my.session;

import java.util.ArrayList;
import java.util.Collection;

import javax.annotation.PostConstruct;
import javax.ejb.LocalBean;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.ejb.StatefulTimeout;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.concurrent.TimeUnit;
import my.entity.ItemEntity;

@LocalBean
@Stateful
@StatefulTimeout(unit = TimeUnit.MINUTES, value = 30)
public class CartBean implements CartItf {

  private Collection<ItemEntity> items;

  @PersistenceContext(unitName = "book-pu")
  private EntityManager itemManager;

  @PostConstruct
  public void initialize() {
    items = new ArrayList<ItemEntity>();
  }

  @Override
  public void addItem(ItemEntity item)
  {
    items.add(item);
  }

  @Override
  public Collection<ItemEntity> getAllItems()
  {
    return items;
  }

  @Remove
  @Override
  public void confirmOrder()
  {
    for (ItemEntity item : items) {
      itemManager.persist(item);
    }
  }
}

Solution

  • you need to ensure to reuse the same stateful and not a new instance each time (each servlet). You have multiple options:

    1. add @SessionScoped and use @Inject instead of @EJB
    2. lookup programmatically the bean instance (new InitialContext()) and put it in the HttpSession