EJB 3.x : Lifecycle and Concurrency models (part 1)
Java EE component life cycle and concurrency related details might not be new for seasoned professionals, but it is something which can take time to sink in for beginners.
As far as EJBs are concerned, understanding their life cycle (and related concurrency scenarios) is extremely critical in order to ensure appropriate usage and solution design using EJBs. It’s easy to misuse them !
I’ll quickly cover Stateless and Stateful beans in this post and skip Lima Beans for the time being!
- Stateful Session Beans – life cycle + concurrency handling
- Stateless beans – concurrency model only, since I had briefly covered the lifecycle in one of my previous posts.
What are the distinct states in the life cycle of a Stateful Session Bean?
- Does Not Exist
- Ready
- Passivated
How do the states change? What triggers them?
Here is a quick tabular snap shot and a high level diagram. For more details, read on . . .
Note: DNE – Does Not Exist, R – Ready, P – Passivated, SFSB – Statelful Session Bean
State Transition | Triggers | Callbacks |
---|---|---|
DNE to R | When a SFSB instance is first accessed via JNDI or DI | @PostConstruct |
R to DNE | Container shuts down, client invokes method annotated with @Remove, the bean reaches idle time out threshold designated by DD or @StatefulTimeout | @PreDestroy |
R to P | The EJB container passivates idle beans and removes them from active memory based on specific algorithms | @PrePassivate |
P to DNE | The bean reaches idle time out threshold designated by DD or @StatefulTimeout | Note: @PreDestroy annotated method is NOT invoked |
P to R | When the client invokes a SFSB instance after it is passivated but has not timed out yet | @PostActivate |
Note: If a SFSB throws an exception during request processing, its instance is destroyed i.e. it goes to a DNE state. The @PreDestroy annotated method is not invoked in this case
Now that we have some idea about the life cycle of a SFSB, let’s try to take a look at how do these beans behave under load i.e. when the application is used by multiple users at a time which translates into concurrent access of SFSB instances
Stateful Session Beans: Concurrency Management
Thread safety is one of the core features of EJBs. The point to be noted is that this thread safety is free of cost and does not need any concurrency related constructs to be coded in by the bean developer himself (there are a few exceptions). As far as SFSB are concerned, the EJB container ensures that only one thread can access a bean instance at a particular time.
In this example, we are trying to simulate concurrent access to a single instance of a SFSB by invoking a test Servlet via JMeter. The Servlet injects the bean via DI and calls a method on it. The SFSB method just uses a Thread.sleep() to pretend as if it’s executing something.
package com.abhirockzz.wordpress.ejb.lifecycle.stateful; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import javax.ejb.Stateful; @Stateful public class MyStatefulBean { public MyStatefulBean() { } public void act() { System.out.println("Entered MyStatefulBean/act() on " + new Date().toString() + " . SFSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException ex) { Logger.getLogger(MyStatefulBean.class.getName()).log(Level.SEVERE, null, ex); } System.out.println("Exit MyStatefulBean/act() on " + new Date().toString() + " . SFSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); } }
package com.abhirockzz.wordpress.ejb.lifecycle.stateful; import java.io.IOException; import java.util.Date; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "SFSBTestServlet", urlPatterns = {"/SFSBTestServlet"}) public class SFSBTestServlet extends HttpServlet { public SFSBTestServlet() { } @Inject MyStatefulBean mySFSB; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Entered SFSBTestServlet/doGet() on " + new Date().toString() + " . Servlet instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); mySFSB.act(); } }
Observations
- Since a Servlet itself is not thread safe, multiple threads will in fact enter the doGet() method
- A single instance of the SFSB (evident via the hashCode result) in being accessed concurrently (see the thread names in the logged statements)
- Only one thread will be able to access the SFSB instance though – other threads wait for their turn while the SFSB method returns. This delay is noticeable via the log statements on the console
What about Stateless Beans?
These beans are inherently thread safe. Why? It is because by default, the container makes sure that each new request is served by a new instance of the bean. Remember, that a client can obtain a reference to stateless bean in 3 possible ways – DI, JNDI or via a remote interface (RMI). In all these cases, it is the container (proxy) which intercepts the call – thus, even if multiple threads are seemingly accessing the same bean instance, its actually not the same one!
package com.abhirockzz.wordpress.ejb.lifecycle.stateless; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import javax.ejb.Stateless; @Stateless public class MyStatelesslBean { public void act() { System.out.println("Entered MyStatelesslBean/act() on " + new Date().toString() + " . SLSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException ex) { Logger.getLogger(MyStatelesslBean.class.getName()).log(Level.SEVERE, null, ex); } System.out.println("Exit MyStatelesslBean/act() on " + new Date().toString() + " . SLSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); } }
package com.abhirockzz.wordpress.ejb.lifecycle.stateless; import java.io.IOException; import java.util.Date; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "SLSBTestServlet", urlPatterns = {"/SLSBTestServlet"}) public class SLSBTestServlet extends HttpServlet { @Inject MyStatelesslBean slsb; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Entered SLSBTestServlet/doGet() on " + new Date().toString() + " . Servlet instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName()); slsb.act(); } }
Observations
- Since a Servlet itself is not thread safe, multiple threads will in fact enter the doGet() method
- Different instances of the SLSB (evident via the hashCode result) are being picked by the container to manage concurrent requests (see the thread names in the logged statements).
- In spite of concurrent requests, each request thread is getting serviced by a new instance
That’s all for now! I am planning to cover Singleton Session beans in a future post. Stay tuned . . . .
Thanks for reading!
Reference: | EJB 3.x : Lifecycle and Concurrency models (part 1) from our JCG partner Abhishek Gupta at the Object Oriented.. blog. |