Enterprise Java

Java EE Stateful Session Bean (EJB) example

In this article we are going to see how you can use Stateful Session Beans to keep track of state across a client Session in a simple Web Application.

1. Introduction

Stateful Session Beans usually hold information about a specific client’s session, and holds that information throughout the whole session (opposed to Stateless Session Beans). A Stateful EJB instance is coupled with only one client. One client can of course have many EJB instances is his session.

In this example we are going to create a simple Shopping Cart application. The session bean will hold a list of products. As the client adds more products to his cart, this list will grow accordingly. Finally the client will be able to checkout his order and the products on the aforementioned list will be persisted in a MySQL database.

To implement the above functionality, we are going to create an EAR Project and an EJB Project that will host our Session Bean and a Dynamic Web Application that will host a Servlet, testing the aforementioned behavior. We are going to use Eclipse Java EE IDE 4,3 Kepler and Glassfish 4.0 as our container. Additionally we are going to use standard JPA 2.o to persist our products in a MySQL 5.6.14 database running on localhost. Here is a guide on how to Integrate MySQL with Glassfish.

2. Create a new Enterprise Application Project

Create a new Enterprise Application Project named StatefulEJBEAR . In Eclipse IDE select File -> New -> Enterprise Application Project, fill in the form and click Finish:

new-ear-project

3. Create a new EJB Projet

Create a new EJB Project called StatefulSessionBeansEJB. We are going to create our session bean on this. Go to File -> New -> EJB Project and fill out the form. Be careful to select “Add EAR Project” and Select “StatefulEJBEAR” as EAR project name:

new-ejb-project

4. Create a Stateful Session Bean

Open StatefulSessionBeansEJB Project in the Project Explorer and in the folder ejbModule create a new source package named com.javacodegeeks.enterprise.ejb. In that package create a new Interface that will be a local view of the EJB:

Cart.java:

package com.javacodegeeks.enterprise.ejb;

import javax.ejb.Local;

import com.javacodegeeks.enterprise.product.Product;

@Local
public interface Cart {

	  void addProductToCart(Product product);

	  void checkOut();

}

As you can see, we declare two methods, one to add a product to the cart and another to checkout the order.

And here is the Session Bean:

CartBean.java:

package com.javacodegeeks.enterprise.ejb;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.ejb.Stateful;
import javax.ejb.StatefulTimeout;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.javacodegeeks.enterprise.product.Product;

@Stateful
@StatefulTimeout(unit = TimeUnit.MINUTES, value = 20)
public class CartBean implements Cart {

	@PersistenceContext(unitName = "pu", type = PersistenceContextType.EXTENDED)
	private EntityManager entityManager;

	private List products;

	@PostConstruct
	private void initializeBean(){
	   products = new ArrayList<>();
	}

	@Override
	public void addProductToCart(Product product) {
		 products.add(product);

	}

	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void checkOut() {
		for(Product product : products){
			entityManager.persist(product);
		}
		products.clear();

	}
}

As you can see, our Session bean implements our Cart interface and simply holds a List of products (more on class Product later).

In the above code notice:

  • We use @Statefulto annotate the class as a Stateful Session Bean.
  • We declare a timeout with @StatefulTimeout(unit = TimeUnit.MINUTES, value = 20) annotation. This timeout denotes the amount of time that the bean should exist, and thus be valid for the session. It should correspond with the HTTP Session timeout value.
  • We use @PersistenceContext to inject an EntityManager that will handle the persistence of our products.
  • We use @PostConstruct annotation on private void initializeBean() method. This will denote to the EJB container to execute that method on bean initialization. You can view it as a constructor.
  • We use @TransactionAttribute(TransactionAttributeType.REQUIRED) annotation on public void checkOut() method. This annotation is required to denote that the container is about to invoke a business method within a transaction context. As you can, see in that method the products on the list are persisted in the database.

5. The Product Entity class

This is the object representing a simple product in our cart application. It consists of an id and a type. As we said, when the order is checked out we want the product on the cart to be persisted in a database. We used JPA 2.0 annotations to map Product class to a MySQL table. For this example, I’ve created a simple database named shop and table named product created with the script:

MySQL product table creation script:

CREATE TABLE `product` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `TYPE` varchar(256) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

So this is the table:

product-table

Let’s see the code of Product class:

Product.java:

package com.javacodegeeks.enterprise.product;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "PRODUCT", catalog = "shop")
public class Product implements Serializable {

  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "ID", nullable = false)
  private int id;

  @Column(name = "TYPE", nullable = false)
  private String type;

  public int getId() {
    return id;
  }

  public String getType() {
    return type;
  }

  public void setType(String description) {
    this.type = description;
  }

}

The above annotations are self explanatory. Very briefly, we use:

  • @Entity to declare the class as an Entity.
  • @Table(name = "PRODUCT", catalog = "shop") to show that the class will be mapped to a table named product in a database named shop.
  • @Id, @GeneratedValue(strategy = GenerationType.IDENTITY) to declare that the field id of the class Product will be the primary key of the corresponding database table.
  • @Column to map a class field to a database column of table product.

Finally, for the persistence to work we need to create a persistence.xml file inside ejbModule/META-INF folder. The file looks like this:

application.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="pu" transaction-type="JTA">

        <jta-data-source>jdbc/MySQLDataSource</jta-data-source> 
        <class>com.javacodegeeks.enterprise.product.Product</class>
    </persistence-unit>

</persistence>

Make sure you’ve integrated MySQL with Glassfish correctly using this quide. For more information on persistence.xml file you can see this Oracle guide.

So the final project structure of StatefulBeansEJB is :

ejb-project-structure

6. Create a new Dynamic Web Project

Go to File -> New -> Dynamic Web Project. Fill out the form and make sure you check “Add project to an EAR” and put StatefulEJBEAR as the “EAR project name”:

new-dynamic-web-project

After clicking “Finish”, go to the project Explorer and Right click on the Project StatefulSessionBeansTest and go to Properties-> Deployment Assembly -> Add -> Porject -> StatefulEJB :

deplyment-assembply

7. Create a new Servlet

Go to StatefulSessionBeansTest Web project and create a new Servlet named ShoppingCartServlet:

new-servlet

So this would be the final structure of the Web Project :

dynamic-web-project-structure

Here is the code of the Servlet:

ShoppingCartServlet.java:

package com.javacodegeeks.enterprise.servlet;

import java.io.IOException;

import javax.naming.InitialContext;
import javax.naming.NamingException;
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 com.javacodegeeks.enterprise.ejb.Cart;
import com.javacodegeeks.enterprise.product.Product;

@WebServlet("/ShoppingCartServlet")
public class ShoppingCartServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;
	private static final String CART_SESSION_KEY = "shoppingCart";

    public ShoppingCartServlet() {
        super();

    }

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

		System.out.println("Hello from servlet");

		Cart cartBean = (Cart) request.getSession().getAttribute(CART_SESSION_KEY);

		 if(cartBean == null){
		      // EJB is not yet in the HTTP session
		      // This means that the client just sent his first request
			  // We obtain a CartBean instance and add it to the session object.
		      try {
		        InitialContext ic = new InitialContext();
		        cartBean = (Cart) 
		         ic.lookup("java:global/StatefulEJBEAR/StatefulSessionBeansEJB/CartBean!"
		         		+ "com.javacodegeeks.enterprise.ejb.Cart");

		        request.getSession().setAttribute(CART_SESSION_KEY, cartBean);

		        System.out.println("shoppingCart created");

		      } catch (NamingException e) {
		        throw new ServletException(e);
		      }
		 }

		 String productName = request.getParameter("product");
		 if(productName != null && productName.length() > 0){

		      Product product = new Product();
		      product.setType(productName);
		      cartBean.addProductToCart(product);

		      System.out.println("product "+productName+" added");
		 }

		 String checkout = request.getParameter("checkout");
		 if(checkout != null && checkout.equalsIgnoreCase("yes")){
		      // Request instructs to complete the purchase
		      cartBean.checkOut();
		      System.out.println("Shopping cart checked out ");
		 }

	}

}

In the above Serlvet, when the user sents a GET request for the fist time, a new CartBean instance will be obtained from the container and added to the session. Then the product query parameter is parsed, and if it’s not null a new Product with type productName, and will e added to the list of products in the session bean.

Then the checkout query parameter is parsed and if it’s evaluated to 'yes' the products in the session bean will be persisted.

Tip: If you are having trouble figuring out the Portable JNDI names for EJB PassivationObject look at the logs or output of Glassfish when deploying the project and you will find a line like this :2013-12-13T18:22:28.598+0200|INFO: EJB5181:Portable JNDI names for EJB PassivationObject: (java:global/StatefulBeans/StatefulEJB/PassivationObject, java:global/StatefulBeans/StatefulEJB/PassivationObject!com.javacodegeeks.enterprise.ejb.Passivation)

8. Test

Now we are simply going to deploy the Dynamic Web Application to Glassfish, and add some products on the cart. Then we will request to checkout the order.

Let’s say we want to add some products, we can issue the following requests :

http://localhost:8080/StatefulSessionBeansTest/ShoppingCartServlet?product=ram
http://localhost:8080/StatefulSessionBeansTest/ShoppingCartServlet?product=mouse
http://localhost:8080/StatefulSessionBeansTest/ShoppingCartServlet?product=ssd

While posting these requests this is the output from the console:

2014-01-07T22:02:07.622+0200|INFO: Hello from servlet
2014-01-07T22:02:07.684+0200|INFO: shoppingCart created
2014-01-07T22:02:07.687+0200|INFO: product ram added
2014-01-07T22:02:12.236+0200|INFO: Hello from servlet
2014-01-07T22:02:12.237+0200|INFO: product mouse added
2014-01-07T22:02:24.851+0200|INFO: Hello from servlet
2014-01-07T22:02:24.851+0200|INFO: product ssd added

Now to checkout the order you can issue:

http://localhost:8080/StatefulSessionBeansTest/ShoppingCartServlet?checkout=yes

This is the output from the console:

2014-01-07T22:19:46.444+0200|INFO: Hello from servlet
2014-01-07T22:19:46.537+0200|INFO: Shopping cart checked out

Here you can see the products on the database :

mysql-terminal

Dowload Eclipse Project

This was an example on Java EE Stateful Session Bean (EJB). Here are the Eclipse Projects of this example : StatefulEJBS.zip

Nikos Maravitsas

Nikos has graduated from the Department of Informatics and Telecommunications of The National and Kapodistrian University of Athens.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

9 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Tomasz Lelek
11 years ago

I think that you should not create resource in get request:

Product product = new Product();
product.setDescription(productName);
shoppingCartBean.addProduct(product);

gmarques
11 years ago
Reply to  Tomasz Lelek

Hello Tomasz,

I agree with you when you say that HTTP GET requests should never change data in the server, but the tutorial is written this way to simplify both the user reading plus the tutorial testing section, ie. focusing on the Stateful Session Bean itself.

Thank you,
Gonçalo

santiago
santiago
10 years ago

Hi, I tried to pass this example to WAS Liberty but I have a trouble.

The lookup is not working, please maybe I am doing some wrong. please can you help me??

Application Error
SRVE0777E: Exception thrown by application class ‘com.javacodegeeks.enterprise.servlet.ShoppingCartServlet.doGet:51’
javax.servlet.ServletException: javax.naming.NameNotFoundException: javax.naming.NameNotFoundException: java:global/StatefulEJBEAR/StatefulSessionBeansEJB/CartBean!com.javacodegeeks.enterprise.ejb.Cart
at com.javacodegeeks.enterprise.servlet.ShoppingCartServlet.doGet(ShoppingCartServlet.java:51)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:575)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1274)
at [internal classes]
Caused by: javax.naming.NameNotFoundException: javax.naming.NameNotFoundException: java:global/StatefulEJBEAR/StatefulSessionBeansEJB/CartBean!com.javacodegeeks.enterprise.ejb.Cart
at com.ibm.ws.jndi.url.contexts.javacolon.internal.JavaURLContext.lookup(JavaURLContext.java:327)
at [internal classes]
at javax.naming.InitialContext.lookup(InitialContext.java:411)
at com.javacodegeeks.enterprise.servlet.ShoppingCartServlet.doGet(ShoppingCartServlet.java:44)
… 4 more

Me too
Me too
10 years ago
Reply to  santiago

I also get the same error. Did you find a way to get pass by this?

Shane
Shane
10 years ago
Reply to  santiago

I figure it out. If you don’t create database, don’t create persistence.xml file and comment out
// @PersistenceContext(unitName = “pu”, type = PersistenceContextType.EXTENDED)
// private EntityManager entityManager;

Prasad
Prasad
10 years ago

How u got servers folder there at first..What is to be included inside that folder?
I followed the same procedure but i dont find web.xml file in StatefullSessionBeansTest/WEB-INF/lib.. Plz tell me how to create web.xml file

Michael
Michael
6 years ago

Nice article! You may want to fix some spelling errors. More specifically the title “4. Create a Sateful Session Bean” must change to “4. Create a Stateful Session Bean” but also the sentence that follows the above title “Create a new Enterprise Application Project named SatefulEJBEAR” must be changed to “Create a new Enterprise Application Project named StatefulEJBEAR”.

Nataly Evagorou
6 years ago
Reply to  Michael

Thank you. We fixed the spelling errors.

Raheem
Raheem
4 years ago

Thanks for the tutorial! I think I’m missing the point of using stateful objects here. I mean, since we save our cart object in our session, couldn’t we just use any stateless object and we can retrieve it again from the session with its previous state? I know that a session object is bound to only one user so we can attach whatever object to it and it will be unique to the session owner. Am I right?

Back to top button