We have asked this question several times in our organisation and I have seen it being asked many times in various web forums.
I suspect that the reason why there is no correct answer is that it all depends on your environment.
Some people report very good results with one approach but the same approach may not be efficient or not even functional somewhere else; such as in a clustered environment.
1. A common basic approach is to store the reference to the remote interface in the http session. Once it has been stored, all clients can quickly access business methods of the stateful session bean. You should use business delegates to communicate with your EJBs.
According to the specification (EJB spec 2.1FR, section 7.8):
There is no fixed mapping between clients and stateless instances. The container simply delegates a client’s work to any available instance that is method-ready.
This means that even with a single remote interface, the container will delegate what bean instance should serve a client call.
With stateful beans the case is the contrary. A remote interface will always guarantee calls are delegated to the same stateful session bean. This of course is crucial to enable a dialog between a client and a stateful bean.
2. The JNDI lookup of a home interface is the costly part of locating an EJB. As such, storing a reference to the home interface is a second approach.
Because application servers may use the create() method as load balancing trigger this approach may behave better in a clustered environment. Consider using the service locator pattern to collect all your service lookups to a single point.
3. A handle to an EJB has the advantage of being serialisable and valid between multiple JVMs. It seems to be the superior way of storing a reference to an EJB.
It is only possible to acquire a handle to EJBs that implement remote interfaces. With EJB 2.0, you will see that many EJBs implement local interfaces only to improve performance. Only the session facades will be exposing both local and remote interfaces.
To be future proof, it is safest to assume as little as possible about the behaviour of your J2EE server and avoiding short cuts.
This will ensure that your application can move from a testing environment to a full clustered, load balanced production environment or a different j2ee application server.
JNDI lookups of the home interface is the expensive operation so implement a service locator that caches home interfaces. Make sure all of your code is using this single point of looking up EJBs. It will then be possible to change the lookup strategy in the future if needed.
The server side
Service Locator pattern
EJB 2.1 Specification