I am planning to use EJBContext to pass some properties around from the application tier (specifically, a message-driven bean) to a persistence lifecycle callback that cannot directly be injected or passed parameters (session listener in EclipseLink, entity lifecycle callback, etc.), and that callback is getting the EJBContext via JNDI.
This appears to work but are there any hidden gotchas, like thread safety or object lifespan that I’m missing? (Assume the property value being passed is immutable like String or Long.)
Sample bean code
@MessageDriven
public class MDB implements MessageListener {
private @Resource MessageDrivenContext context;
public void onMessage(Message m) {
context.getContextData().put("property", "value");
}
}
Then the callback that consumes the EJBContext
public void callback() {
InitialContext ic = new InitialContext();
EJBContext context = (EJBContext) ic.lookup("java:comp/EJBContext");
String value = (String) context.getContextData().get("property");
}
What I’m wondering is, can I be sure that the contextData map contents are only visible to the current invocation/thread? In other words, if two threads are running the callback method concurrently, and both look up an EJBContext from JNDI, they’re actually getting different contextData map contents?
And, how does that actually work – is the EJBContext returned from the JNDI lookup really a wrapper object around a ThreadLocal-like structure ultimately?
I think in general the contract of the method is to enable the communication between interceptors + webservice contexts and beans. So the context should be available to all code, as long as no new invocation context is created. As such it should be absolutely thread-safe.
Section 12.6 of the EJB 3.1 spec says the following:
Furthermore, the getContextData method is described in 4.3.3:
In terms of actual implementation, JBoss AS does the following:
Where the
CurrentInvocationContextuses a stack based on a thread-local linked list to pop and push the current invocation context.See org.jboss.ejb3.context.CurrentInvocationContext. The invocation context just lazily creates a simple
HashMap, as is done in org.jboss.ejb3.interceptor.InvocationContextImplGlassfish does something similar. It also gets an invocation, and does this from an invocation manager, which also uses a stack based on a thread-local array list to pop and push these invocation contexts again.
The JavaDoc for the GlassFish implementation is especially interesting here:
Just as in JBoss AS, GlassFish too lazily creates a simple
HashMap, in this case in com.sun.ejb.EjbInvocation. Interesting in the GlassFish case is that the webservice connection is easier to spot in the source.