Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The Shopping Cart dilemma in JavaEE

I am working on an online store using Java EE stack. For presentation, I am using JSF 2.2, JPA for persistence and EJB 3.x for business logic, (where x >= 1) and JAX-RS for services as there will be a mobile application as well.

The actual problem is the implementation of the Shopping Cart. At one point, the Shopping Cart can be a SFSB (Stateful Session Bean). This means that the EJB container will treat the servlet/web container as a client and manage conversation between the 2 containers. How would I then identify the end-user (actual client) who is adding items to a shopping cart if the EJB client is a servlet/web container?

My second option is to store the SFSB into an HttpSession. This way, the end-user can retrieve the cart from the HttpSession (and the session is already managed from the servlet container). How will this impact the transactional states of the EJB conversation?

The shopping cart is persisted in a database using JPA.

Is there anything else that I need to take into consideration?

Thanks.

like image 582
Buhake Sindi Avatar asked Jan 06 '14 17:01

Buhake Sindi


1 Answers

Actually this is a very good question.

I'm using the same exact stack (Java EE 7,Glassfish 4, JSF 2.2, EclipseLink JPA, EJB 3.1) for 70% of my development activity, and I frequently develop custom eCommerce sites so I'm familiar with the design of shopping carts.

The two approaches I have followed (before finally deciding for one of the two):

  • Stateful Session EJB, implementing a @Remote plain Java interface which defines the businness logic
  • SessionScoped ManagedBean and Stateless EJB implementing a @Local interface which defines the business logic.

I personally started with the first approach, but I recently switched to the second. I'll explain why, later in this answer.

This first approach is really simple. You just need a plain interface with a @Remote annotation and a @Stateful Session Bean implementing it. Then in your backing bean you can inject the interface via CDI using the @EJB annotation instead of the @Inject one to leverage all the nice features of EJB injections like pooling. For you cart I would create:

1) an interface named ShoppingCart.java:

@Remote
public interface ShoppingCart{

  public void init(Integer id);
  public void addToCart(String product);

}

2) a Stateful Session EJB named ShoppingCartImpl.java implementing the ShoppingCart.java interface

 @Stateful
 public class ShoppingCartImpl implements ShoppingCart{

   private Integer uid;
   private ArrayList<String> products;

   @PostConstruct
   private void create(){
     producs = new ArrayList<String>();
   }

   @Override
   public void init(Integer id){
     if(id==null){
       uid = id;
     }
  }

  @Override
  public void addToCart(String product){
     if(product!=null){
        products.add(product);
     }

 }


 }

A client class can access the Statefull Session Bean using CDI via the @EJB annotation.

public class ShoppingCartClient {

    @EJB
    private static ShoppingCart cart;

     // your methods here, using the ShoppingCart interface

}

The actual "link" between the physical customer and its instance of the ShoppingCart implementation instance is assured because each client is associated with a new instance of a stateful session bean. From the client's perspective, the business methods appear to run locally, although they run remotely in the session bean. For the records...Oracle in his own tutorials suggests this approach.

MY PREFERRED APPROACH

My preferred approach is to use a SessionScoped JSF backing bean representing the code, and using a Stateless EJB to access the needed business logic. An interface is needed in this case too, but it can be made Local, changing the code in something like that:

1) Java Local interface

@Local
public interface ShoppingCart{
   public void doSomething(List<Product> list);
}

2) Stateless EJB

@Stateless
public class ShoppingCartImpl implements ShoppingCart{

   @Override
   public void doSomething(List<Product> list){
    // persistence, tax calculation, etc
   }
}

3) JSF Session Scoped Bean

@ManagedBean
@SessionScoped
public class CartBean {

 private List<Product> products = new ArrayList<Product>();

 public void add(Product product) {
     products.add(product);
 }

 public void remove(Product product) {
     products.remove(product);
 }

 public List<Product> getProducts() {
     return products;
 }
}

Second approach has the following benefits:

  • Stateless EJB are faster then Stateful EJB
  • Stateless EJB support pooling by design

The memory footprint is the same as the first case since JSF stores session scoped managed beans in the same way Stateful Session Beans are stored.

Stateful EJBs are suited if you want to be able to use it somewhere else or to share them among different webapp.

like image 174
elbuild Avatar answered Sep 17 '22 13:09

elbuild