This week I had a doubt about removing Stateful Session Beans (SFSB). Working on a heavy loaded EJB3 application with about 3K objects been created in a single operation I had to watch very closely the performance of my application. The doubt was: "In a Stateful Session Bean which uses another Stateful Session Bean that was injected by the container has to be removed explicitly or the container removes it as soon the first bean is removed?" Sounds complicated but isn't I will give a example.

If you have studied the life cycle of a Stateful Bean you already knows that as soon the container creates a instance of the Bean it starts to fulfill the Beans dependencies. Here's a simple Bean called SFSBFirst, here's the interface:

 package br.com.svvs;
import javax.ejb.Remote;

@Remote
public interface SFSBFirst {
 void call(String message);
 void remove();
}  

And here is the implementation:

package br.com.svvs;

import javax.annotation.PreDestroy;
import javax.ejb.EJB;
import javax.ejb.Remove;
import javax.ejb.Stateful;

@Stateful
public class SFSBFirstBean implements SFSBFirst {

 @EJB private SFSBSecond ssbSecond;

 private String message;

 @Override
 public void call(String message) {
     this.message = message;
     System.out.println(SFSBFirst.class.getName() + ": called(" + message + ")");
     ssbSecond.call(message);
 }

 @Override
 @Remove
 public void remove() {
     System.out.println(SFSBFirst.class.getName() + ": removing(" + this.message + ")");
     //ssbSecond.remove();
 }

 @PreDestroy
 public void preDestroy() {
     System.out.println(SFSBFirst.class.getName() + ": destroying(" + this.message + ")");
 }

}

This bean declares two exposed methods call(String message) and remove(), and ask for the container to inject the bean SFSBSecond. It also annotate a method with @PreDestroy which makes the container call it before garbage collect the bean.

It's a simple bean just to check if I have to call remove() on SFSBSecond or not. So let's look on SFSBSecond interface:

package br.com.svvs;

import javax.ejb.Remote;

@Remote
public interface SFSBSecond {
 void call(String message);
 void remove();
}  

And the implementation:

package br.com.svvs;

import javax.annotation.PreDestroy;
import javax.ejb.Remove;
import javax.ejb.Stateful;

@Stateful
public class SSBSecondBean implements SFSBSecond {

 private String message;

 @Override
 public void call(String message) {
     this.message = message;
     System.out.println(SFSBSecond.class.getName() + ": called(" + this.message + ")");
 }

 @Override
 @Remove
 public void remove() {
     System.out.println(SFSBSecond.class.getName() + ": removing(" + this.message + ")");
 }

 @PreDestroy
 public void preDestroy() {
     System.out.println(SFSBSecond.class.getName() + ": destroying(" + this.message + ")");
 }

}  

Still a simple bean too, and now let's look at the test code:

import javax.naming.InitialContext;
import javax.naming.NamingException;

import br.com.svvs.SFSBFirst;

public class Test {
 public static void main(String[] args) throws NamingException {
     InitialContext ic = new InitialContext();
 
     SFSBFirst ssb = (SFSBFirst) ic.lookup("SFSBFirstBean/remote");
 
     ssb.call("Hello World!");
     ssb.remove();
 }

}  

Results

I used JBoss 4.2.2 GA on my tests, the first run I did not removed explicitly the injected bean. Here's the output:

01:09:11,089 INFO  [STDOUT] br.com.svvs.SFSBFirst: called(Hello World!)
01:09:11,142 INFO  [STDOUT] br.com.svvs.SFSBSecond: called(Hello World!)
01:09:11,161 INFO  [STDOUT] br.com.svvs.SFSBFirst: removing(Hello World!)
01:09:11,162 INFO  [STDOUT] br.com.svvs.SFSBFirst: destroying(Hello World!)

As you can see SFSBSecond was not removed. So if I want to remove it, I have to explicitly call remove() on the second bean during the remove() of the first bean. The second run was made calling remove():

01:11:51,702 INFO  [STDOUT] br.com.svvs.SFSBFirst: called(Hello World!)
01:11:51,706 INFO  [STDOUT] br.com.svvs.SFSBSecond: called(Hello World!)
01:11:51,714 INFO  [STDOUT] br.com.svvs.SFSBFirst: removing(Hello World!)
01:11:51,719 INFO  [STDOUT] br.com.svvs.SFSBSecond: removing(Hello World!)
01:11:51,721 INFO  [STDOUT] br.com.svvs.SFSBSecond: destroying(Hello World!)
01:11:51,721 INFO  [STDOUT] br.com.svvs.SFSBSecond: destroying(Hello World!)
01:11:51,721 INFO  [STDOUT] br.com.svvs.SFSBFirst: destroying(Hello World!)

This time SFSBSecond was removed, but I am not sure why it appears to be called two times, probably a JBoss bug.

Since a second opinion is always good let's see how the glassfish v2 application server behaves. The first test was not calling remove() on SFSBSecond:

[#|2008-10-29T02:37:08.263-0200|INFO|br.com.svvs.SFSBFirst: called(Hello World!)|#]
[#|2008-10-29T02:37:08.264-0200|INFO|br.com.svvs.SFSBSecond: called(Hello World!)|#]
[#|2008-10-29T02:37:08.267-0200|INFO|br.com.svvs.SFSBFirst: removing(Hello World!)|#]
[#|2008-10-29T02:37:08.267-0200|INFO|br.com.svvs.SFSBFirst: destroying(Hello World!)|#]

Well, as the JBoss server Glassfish doesn´t remove the inject SFSBSecond bean. And now calling remove() explicitly:

[#|2008-10-29T01:50:28.722-0200|INFO|br.com.svvs.SFSBFirst: called(Hello World!)|#]
[#|2008-10-29T01:50:28.722-0200|INFO|br.com.svvs.SFSBSecond: called(Hello World!)|#] [#|2008-10-29T01:50:28.725-0200|INFO|br.com.svvs.SFSBFirst: removing(Hello World!)|#]
[#|2008-10-29T01:50:28.726-0200|INFO|br.com.svvs.SFSBSecond: removing(Hello World!)|#] [#|2008-10-29T01:50:28.726-0200|INFO|br.com.svvs.SFSBSecond: destroying(Hello World!)|#]
[#|2008-10-29T01:50:28.726-0200|INFO|br.com.svvs.SFSBFirst: destroying(Hello World!)|#] 
That appears to be right, the bean was removed in the right order and all variables still in memory.

Conclusion

Explicitly remove the injected beans, the application servers JBoss and Glassfish will not do it for you. I think that´s kind odd, since they injected the Bean and a Stateful bean can have only one client it must be destroyed when the client is destroyed, but that´s the way things are.