EJB passivation and activation example
In this tutorial we are going to see how activation and passivation works in a Stateful Java Enterprise Session Bean.
1. Introduction
Stateful Session Beans usually hold information about a specific client, and holds that information throughout the whole session. It is a fact though, that client sessions tend to be active for a respectable amount of time, and of course many clients can be online the same time. As a result, it is a necessity for the EJB container to implement a mechanism that makes possible the releasing of resources that are not used at a given moment, and can be activated again when needed.
Passivating a Session Bean means removing it from the Session EJB Cache of the container and storing all the necessary information and properties of the session bean in a file. Activating a session bean means reading the aforementioned file and restoring the passivated session bean in the cache. As you might imagine all the fields of a that session bean must be Serializable.
In this example 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 passivation and activation properties. We are going to use Eclipse Java EE IDE 4,3 Kepler and Glassfish 4.0 as our container.
2. Create a new Enterprise Application Project
Create a new Enterprise Application Project named SatefulBeansEAR
.In Eclipse IDE select File -> New -> Enterprise Application Project and fill in the form and click Finish:
3. Create a new EJB Projet
Create a new EJB Project called StatefulEJB. 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 “StatefulBeansEAR
” as EAR project name:
Click Next twice and choose to create EJB Client JAR, as well as to generate the ejb-jar.xml
deployment descriptor :
4. Create a Sateful Session Bean
Open StatefulEJB 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:
Passivation.java:
package com.javacodegeeks.enterprise.ejb; import javax.ejb.Local; import com.javacodegeeks.enterprise.ejb.property.PropertyObject; @Local public interface Passivation { void setPropertyObject(PropertyObject propertyObject); PropertyObject getPropertyObject(); }
And here is the Session Bean:
PassivationBean.java:
package com.javacodegeeks.enterprise.ejb; import javax.ejb.PostActivate; import javax.ejb.PrePassivate; import javax.ejb.Stateful; import com.javacodegeeks.enterprise.ejb.property.PropertyObject; @Stateful public class PassivationBean implements Passivation { private PropertyObject myProperty; @Override public void setPropertyObject(PropertyObject propertyObject) { this.myProperty = propertyObject; } @Override public PropertyObject getPropertyObject() { return this.myProperty; } @PrePassivate private void prePassivate(){ // Free resources // ... System.out.println("Passivating EJB. Property value: " + myProperty.getProperty()); } @PostActivate private void postActivate(){ // Reactivate resources // ... System.out.println("Activating EJB. Property value: " + myProperty.getProperty()); } }
In the above code :
private void prePassivate()
: Annotated with@PrePassivate
is the method to be executed when the EJB container decides to passivate that session bean.private void postActivate()
: Annotated with@PostActivate
is the method to be executed when the EJB container activates a passivated session bean because it’s needed again.
Additionally, as you can see the session bean has a private PropertyObject myProperty
field. This is an object that can contain information for the session, for the bean, for the resources and any kind of data one can find useful.
So when the bean is passivated we want this property has to be saved and restored as well. Thus, it must be Serilizable
as we mentioned in the Introduction. Having said that, if you don’t want to store this property, because you don’t care to retrieve that resource when the bean is activated, you can declare the object transient using the @Transient
annotation . For this Object I’ve created a new source Package named com.javacodegeeks.enterprise.ejb.property
.
PropertyObject.java:
package com.javacodegeeks.enterprise.ejb.property; import java.io.Serializable; public class PropertyObject implements Serializable { private static final long serialVersionUID = 1L; private String property; public PropertyObject(String value){ this.property = value; } public String getProperty() { return property; } }
So this would be the final structure of the EJB project SatefulEJB
:
5. 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 StatefulBeans EAR as the “EAR project name”:
After clicking “Finish”, go to the project Explorer and Right click on the Project StatefulBeansTest
and go to Properties-> Deployment Assembly -> Add -> Porject -> StatefulEJB :
6. Create a new Servlet
Go to StatefulBeansTest
Web project and create a new Servlet named MyServlet
:
So this would be the final structure of the Web Project :
MyServlet.java:
package com.javacodegeeks.enterprise.servlet; import java.io.IOException; import javax.naming.InitialContext; 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.Passivation; import com.javacodegeeks.enterprise.ejb.property.PropertyObject; @WebServlet("/MyServlet") public class MyServlet extends HttpServlet { private static final long serialVersionUID = 1L; public MyServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Hello from Servlet"); InitialContext ic; Passivation passivation; String beanCountparam = request.getParameter("count"); if (beanCountparam != null) { int beanCount = Integer.parseInt(beanCountparam); try { ic = new InitialContext(); for (int i = 0; i < beanCount; i++) { passivation = (Passivation) ic .lookup("java:global/StatefulBeansEAR/StatefulEJB/PassivationBean!" + "com.javacodegeeks.enterprise.ejb.Passivation"); passivation.setPropertyObject(new PropertyObject( "bean" + i)); request.getSession().setAttribute("bean" + i, passivation); } } catch (Exception e) { throw new ServletException(e); } } String beanActivationIndex = request.getParameter("activate"); if (beanActivationIndex != null) { try { ic = new InitialContext(); passivation = (Passivation) request.getSession() .getAttribute("bean" + beanActivationIndex); System.out.println("TestObject property value: " + passivation.getPropertyObject().getProperty()); } catch (Exception e) { throw new ServletException(e); } } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
In the above code, when the Servlet is accessed, it parses the count
query parameter, generates count
session beans and stores them to the request session. Accordingly, if the activate
query parameter is present the corresponding bean is retrieved from the session.
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)
7. Test
After creating the above projects you have to Run StatefulBeansTest
on Glassfish.
To test the desired behavior, we have to somehow trigger the passivation and activation of a certain session bean. To force a Session bean to be passivated we have to make the container remove it from the session cache. Glassfish can host 512 session beans at most, by default. So if we trigger a request asking for 600 beans to be created, some of them will be eventually removed from the cache.
This request can be :
http://localhost:8080/StatefulBeansTest/MyServlet?count=600
And the output in the console will be :
2013-12-30T22:29:36.978+0200|INFO: Hello from Servlet
2013-12-30T22:29:37.315+0200|INFO: Passivating EJB. Property value: bean31
2013-12-30T22:29:37.329+0200|INFO: Passivating EJB. Property value: bean64
2013-12-30T22:29:37.332+0200|INFO: Passivating EJB. Property value: bean70
2013-12-30T22:29:37.345+0200|INFO: Passivating EJB. Property value: bean98
2013-12-30T22:29:37.390+0200|INFO: Passivating EJB. Property value: bean117
2013-12-30T22:29:37.390+0200|INFO: Passivating EJB. Property value: bean116
2013-12-30T22:29:37.390+0200|INFO: Passivating EJB. Property value: bean115
2013-12-30T22:29:37.394+0200|INFO: Passivating EJB. Property value: bean114
2013-12-30T22:29:37.394+0200|INFO: Passivating EJB. Property value: bean113
2013-12-30T22:29:37.394+0200|INFO: Passivating EJB. Property value: bean112
2013-12-30T22:29:37.397+0200|INFO: Passivating EJB. Property value: bean111
2013-12-30T22:29:37.398+0200|INFO: Passivating EJB. Property value: bean110
2013-12-30T22:29:37.399+0200|INFO: Passivating EJB. Property value: bean109
2013-12-30T22:29:37.402+0200|INFO: Passivating EJB. Property value: bean108
2013-12-30T22:29:37.403+0200|INFO: Passivating EJB. Property value: bean107
.
.
.
So as you can see some of the beans are removed from the cache and thus get passivated. Now to trigger the activation of a bean we can simply request to retrieve a bean that is already passivated, for example bean31
.
This request can be:
http://localhost:8080/StatefulBeansTest/MyServlet?activate=31
And the output in the console will be :
2013-12-30T22:33:48.742+0200|INFO: Hello from Servlet
2013-12-30T22:33:48.744+0200|INFO: Activating EJB. Property value: bean31
2013-12-30T22:33:48.745+0200|INFO: TestObject property value: bean31
That’s it. Now to be convinced that the session beans we actually serialized to the disc you can go to Glassfish_installation_folder/glassfish4/glassfish/domains/domain1/session-store
where you can actually see the folder that the beans were saved:
Download Eclipse Project
This was an example on EJB passivation and activation. Download the Eclipse Project of this tutorial : EJBPassivationActivation.zip
Hello Goncalo! Thanks for the tutorial, very useful one!
I have one question, when the EJB container does the passivation, where does it passivate the object to? Memory? Serialized file? Or may it mix both? I’m asking because I have an ADF BC background and I’m planning to do some comparisons using both tecnologies, in ADF BC the passivation occurs to a database table called PS_TXN.
Regards,
Renan.
Hello Renan,
That’s a subject that would definitely worth a full blog post on it’s own. There are many possible configurations that may affect the container passivation mechanism. By default the container passivates to the file system but there are other details that may come into play. I suggest you to go through documentation related with the following subjects (regarding EJB): Availability, Session Store Location and Passivation Stores.