EJB passivation and activation example

05 August 2013
By Gonçalo Marques
In this tutorial we will cover Java EE Stateful Session Bean passivation and activation mechanism.

Introduction

Stateful Session Beans are usually coupled with some given client and - as the name states - they keep state across client invocations.

Since a conversation with a client may be long-lived, and many simultaneous clients may exist at the same time, the EJB container must have a mechanism of releasing resources that may not be needed at some precise moment.

This is where EJB passivation and activation comes into: The container may passivate an EJB that is not needed at some point in time and later activate it (or recover it) as soon as it's needed again.

This tutorial considers the following environment:

  1. Ubuntu 12.04
  2. JDK 1.7.0.21
  3. Glassfish 4.0

The Stateful Session Bean

We start this tutorial by defining the Stateful Session Bean:

TestingPassivationBean.java

package com.byteslounge.ejb;

import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.ejb.Stateful;

import com.byteslounge.model.TestingObject;

@Stateful
public class TestingPassivationBean implements TestingPassivation {

  private TestingObject testingObject;
  
  @Override
  public void setTestingObject(TestingObject testingObject) {
    this.testingObject = testingObject;  
  }

  @Override
  public TestingObject getTestingObject() {
    return testingObject;
  }
  
  @PrePassivate
  private void prePassivate(){
    // Free resources (ex: JDBC connections)
    // ...
    
    System.out.println("Passivating EJB. Property value: " 
      + testingObject.getTestingProperty());
  }
  
  @PostActivate
  private void postActivate(){
    // Reinitialize resources (ex: JDBC connections)
    // ...
    
    System.out.println("Activating EJB. Property value: " 
      + testingObject.getTestingProperty());
  }

}

The EJB itself is kept as simple as possible so we may focus in the relevant tutorial details. Method prePassivate is annotated with @PrePassivate. This annotation instructs the EJB container to call this method before passivating the EJB.

Method postActivate is annotated with @PostActivate. This annotation instructs the EJB container to call this method after activating - or recovering - the EJB.

EJB passivation should be used to release any resources the EJB may be keeping like JDBC connections, references to files, JMS listeners, among others. During EJB activation the resources should be reinitialized if needed.

EJB properties - in this case an instance of type TestingObject - will also be passivated and recovered during bean passivation/activation by default. Keep in mind that EJB passivation is actually implemented with Java serialization so all EJB properties must be serializable (or transient if we don't want them to be kept after passivation/activation). We will cover the TestingObject type in the next section.

Finally the EJB is implementing an interface that is a simple Local view of the EJB:

TestingPassivation.java

package com.byteslounge.ejb;

import javax.ejb.Local;

import com.byteslounge.model.TestingObject;

@Local
public interface TestingPassivation {

  void setTestingObject(TestingObject testingObject);
	
  TestingObject getTestingObject();

}

The TestingObject type

As we have seen in the previous section our testing EJB has a property of type TestingObject. This class is defined as:

TestingObject.java

package com.byteslounge.model;

import java.io.Serializable;

public class TestingObject implements Serializable {

  private static final long serialVersionUID = 1L;
  
  private String testingProperty;
  
  public TestingObject(String testingProperty){
    this.testingProperty = testingProperty;
  }

  public String getTestingProperty() {
    return testingProperty;
  }

}

This is the definition of the EJB testingObject property and it will be used to illustrate that the serializable EJB properties are kept after EJB passivation and activation. Once again if you don't want your EJB properties to be kept after EJB passivation and activation you should declare them as transient.

Testing

Now that we have our EJB ready to be tested let's do some tuning of the EJB container in order to force EJB passivation to occur as soon as possible.

Since we are using Glassfish as the EJB container, there is a configuration file that may be used to change it's default settings:

sun-ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD 
Application Server 9.0 EJB 3.0//EN" 
"http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
<sun-ejb-jar>
  <enterprise-beans>
    <ejb>
      <ejb-name>TestingPassivationBean</ejb-name>
      <bean-cache>
        <max-cache-size>5</max-cache-size>
        <cache-idle-timeout-in-seconds>10</cache-idle-timeout-in-seconds>
      </bean-cache>
    </ejb>
  </enterprise-beans>
</sun-ejb-jar>

The sun-ejb-jar.xml is a configuration file that may be used along with Glassfish in order to change the EJB container default settings. We are setting both the EJB maximum cache size and idle timeout to very low settings so it becomes easy to force the container to start passivating EJBs.

Now let's define a testing Servlet to create multiple EJB instances:

TestingServlet.java

package com.byteslounge.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.byteslounge.ejb.TestingPassivation;
import com.byteslounge.model.TestingObject;

@WebServlet(name = "testingServlet", urlPatterns = {"/testing"})
public class TestingServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;

  protected void doGet(HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException {
    
    InitialContext ic;
    TestingPassivation testingPassivation;
    
    String beanCountParam = request.getParameter("count");
    
    if(beanCountParam != null){
      int beanCount = Integer.parseInt(beanCountParam);
      
      try{
        ic = new InitialContext();
        for(int i = 0; i < beanCount; i++){
          testingPassivation = (TestingPassivation)
            ic.lookup("java:global/com-byteslounge/com-bytesloungeejb/TestingPassivationBean");
          
          testingPassivation.setTestingObject(new TestingObject("bean" + i));
          
          request.getSession().setAttribute("bean" + i, testingPassivation);
        }
      } catch(Exception e){
        throw new ServletException(e);
      }
    }
    
    String beanIndex = request.getParameter("activate");
    
    if(beanIndex != null){
      try{
        ic = new InitialContext();
      
        testingPassivation = (TestingPassivation)
          request.getSession().getAttribute("bean" + beanIndex);
        
        System.out.println("TestingObject property value: " 
          + testingPassivation.getTestingObject().getTestingProperty());
        
      } catch(Exception e){
        throw new ServletException(e);
      }
    }
  }
}

This Servlet accomplishes a couple of distinct tasks:

The first is creating EJBs if the parameter count is present in the request parameters. It will create as man EJBs as specified in the parameter and store all EJB instances in the HTTP session.

The second is to fetch an EJB from the HTTP session if the parameter activate is present in the request parameters. It will fetch the instance which index is specified in the parameter.

After deploying the application in the container when we request the following URL:

http://localhost:8080/byteslounge/testing?count=100

The container will create 100 instances of our EJB. Since we configured the EJB container cache and timeout to very low settings we will see very soon in the server logs evidence of EJB passivation:

Passivating EJB. Property value: bean13
Passivating EJB. Property value: bean12
...
Passivating EJB. Property value: bean11

Now if we request the following URL:

http://localhost:8080/byteslounge/testing?activate=12

The EJB which property value was bean12 was previously passivated as we have seen in the logs. As soon as we invoke an EJB method it will be activated and the following will be visible in the server logs:

Activating EJB. Property value: bean12
TestingObject property value: bean12

Troubleshooting

If you are also running Glassfish and notice the following error message in your server logs:

Error during passivation of [YOUR_BEAN_NAME]

Make sure that all your EJB properties hierarchy are implementing the Serializable interface (or declare them as transient if you don't need the properties to be kept after bean passivation and serialization).

Downloadable sample

The downloadable code sample at the end of this page contains the full source code used in this example.

Download source code from this article

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with 8+ years of experience in software development and architecture design. During this period his main focus was delivering software solutions both in banking and telecommunications area. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

He is also the author of the WiFi File Browser Android application: