Have you ever wondered how stateless an EJB3 Stateless Session Bean is? Well, I have, I wrote a test program and I got some interesting and – for me – surprising results, that I wanted to share.
My original understanding (or let’s call it mis-understanding) was, that the JEE server has a pool of EJB instances for each bean class, that you get one of those beans injected for each @EJB annotation, and that all those injections happen immediately upon entrance into the container via a top-level call.
A call to a service bean via SOAP is such a top-level call. I expected, that the injections not only happen in the called bean, but also somehow happen along the whole call graph, meaning that not only EJB references in the top-level service bean would be satisfied via injection, but also in beans referenced by that bean and so on.
Finally I expected that if two beans reference the same type of bean, they would actually get the same instance. I really have never thought the whole thing through, I simply expected some diffuse sort of magic.
The idea was, that some ServiceBean would get a value, either via parameters or from elsewhere (actually I thought of HTTP headers), store that information in some StorageBean, and then call a WorkerBean. The WorkerBean or some other code down the call graph would then retrieve that information from the StorageBean. In order for this to work, the StorageBean injected into the ServiceBean would have to be the same instance as the StorageBean injected into the WorkerBean, and the StorageBean would have to be exclusively allocated to that one thread for the whole duration of the top-level call, or else there would be a time interval between when the value gets being set and the time when it is read in the WorkerBean, leading to the usual multi-threading troubles.
I wanted a StorageBean to have some instance variables, wanted to fill them somewhere in the program, and wanted to read them elsewhere, without having to pass them around as parameters. In other words, I wanted a Stateless Session Bean to have some state, and I hoped that the container would guarantee consistency of this state for the time of one top-level call.
I’m pretty sure this is still not clear, so let’s look at some code. Here is a Singleton that generates random numbers:
package com.manessinger.test.stateless.backing;
import java.util.Random;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.LocalBean;
@LocalBean
@Singleton
public class RandomGen {
private Random r;
@PostConstruct
public void init() { r = new Random(); }
public int nextInt() { return r.nextInt(); }
} |
The StorageBean could be something like
package com.manessinger.test.stateless.backing;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
@LocalBean
@Stateless
public class StorageBean {
private int value;
public void setValue(int value) { this.value = value; }
public int getValue() { return value; }
} |
A first shot at the ServiceBean could be
package com.manessinger.test.stateless.service;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.jws.WebService;
import com.manessinger.test.stateless.backing.RandomGen;
import com.manessinger.test.stateless.backing.StorageBean;
import com.manessinger.test.stateless.backing.WorkerBean;
@Stateless
@WebService
public class ServiceBean {
@EJB StorageBean s;
@EJB WorkerBean w;
@EJB RandomGen rand;
public boolean test1() {
int i = rand.nextInt();
s.setValue(i);
boolean isCorrect = w.verify1(i);
return isCorrect;
}
} |
and here is finally the worker:
package com.manessinger.test.stateless.backing;
import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
@LocalBean
@Stateless
public class WorkerBean {
@EJB StorageBean s;
public boolean verify1(int i) {
int value = s.getValue();
boolean isCorrect = (value == i);
if (!isCorrect) {
System.err.println(String.format("Error: expected %d, got %d", value, i));
}
return isCorrect;
}
} |
Thus the ServiceBean retrieves a random value, uses it to set the value of the StorageBean, and then passes the value into a verify() method of the worker. The worker retrieves the value from the StorageBean, compares it with its parameter, and returns true if they are the same value.
Try it for yourself: call the ServiceBean from the web service tester in the GlassFish Administration Console and it will just work. It’s EJB3 and EJBs are just POJOs after all, right?
Wrong. Sure, it does work, but now try to call the test method from a load testing application like Apache Jakarta JMeter. Try a thread group with 15 threads, a ramp-up period of one second, and 50 iterations. This makes for a total of 750 calls with a high degree of parallelity. Now look at the server log and you will see lines like
...
SEVERE: Error: expected 781730157, got -1784951881
SEVERE: Error: expected 781730157, got -1093939097
SEVERE: Error: expected -1093939097, got 540930629
SEVERE: Error: expected -1093939097, got 1481982330
... |
Funny, huh? And look at the numbers: this is a typical effect in a program that is not thread-safe. Consecutive calls get mixed up.
My next idea was a variant:
public boolean test2() {
int i = rand.nextInt();
s.setValue(i);
boolean isCorrect = w.verify2(i, s);
return isCorrect;
} |
and
public boolean verify2(int i, StorageBean sParam) {
int value = sParam.getValue();
boolean isCorrect = (value == i);
if (!isCorrect) {
System.err.println(String.format("Error: expected %d, got %d", value, i));
}
return isCorrect;
} |
This means that I don’t let the StorageBean be injected into the worker, instead I pass it down as a parameter. Funnily enough, this did not work either. Under load it showed the same effect. Only when I finally passed the value in something that was really only a POJO, it did work:
package com.manessinger.test.stateless.backing;
public class StorageNonBean {
private int value;
public void setValue(int value) { this.value = value; }
public int getValue() { return value; }
} |
public boolean test3() {
int i = rand.nextInt();
StorageNonBean snb = new StorageNonBean();
snb.setValue(i);
boolean isCorrect = w.verify3(i, snb);
return isCorrect;
} |
and
public boolean verify3(int i, StorageNonBean snbParam) {
int value = snbParam.getValue();
boolean isCorrect = (value == i);
if (!isCorrect) {
System.err.println(String.format("Error: expected %d, got %d", value, i));
}
return isCorrect;
} |
Of course the latter is trivial, but the consequences of this little test are clear:
Stateless Session Beans are really meant to be stateless. In fact the EJB 3.0 final specification says in section 4.5 that:
Because all instances of a stateless session bean are equivalent, the container can choose to delegate a client-invoked method to any available instance. This means, for example, that the container may delegate the requests from the same client within the same transaction to different instances, and that the container may interleave requests from multiple transactions to the same instance.
The only thing that the container guarantees, is that one bean will be exclusively allocated to a transaction (and therefore a thread) for the time of the invocation of one of its own methods. This does not extend to other EJBs that it calls. There is no guarantee that we call the same instance of StorageBean, if we call storage.setValue() and storage.getValue() even consecutively from the same ServiceBean, one call immediately after the other, and even if it were the same instance, there is no guarantee, that the storage has not been modified by another instance of the service bean.
Why did I ever get the idea that this construct could work? Well, probably it’s the fact, that the injection of a persistence context always gives you access to the same context (or at least the same data). I don’t know, but in any case I found it important to clarify things, because I have the impression that my mis-understanding is not completely uncommon, and when you fall into that pit, you may not recognize it until your system is under heavy load.