Adam Bien's Weblog
How To Pass Context In Standard Way - Without ThreadLocal
javax.transaction.TransactionSynchronizationRegistry holds a Map-like structure and can be used to pass state inside a transaction. It works perfectly since the old J2EE 1.4 days and is thread-independent.
Because an Interceptor is executed in the same transaction as the ServiceFacade, the state can be even set in a @AroundInvoke method. The TransactionSynchronizationRegistry (TSR) can be directly injected into an Interceptor:
public class CurrentTimeMillisProvider {
@Resource
private TransactionSynchronizationRegistry registry;
@AroundInvoke
public Object injectMap(InvocationContext ic) throws Exception{
registry.putResource(KEY, System.currentTimeMillis());
return ic.proceed();
}
}
A ServiceFacade don't even has to inject the TSR. The state is automatically propagated to the invoked service:
@Stateless
@WebService
@Interceptors(CurrentTimeMillisProvider.class)
public class ServiceFacadeBean implements ServiceFacade {
@EJB
private Service service;
public void performSomeWork(){
service.serviceInvocation();
}
}
Everything, what is invoked in the scope of a ServiceFacade - and so its transaction has access to the state stored in the injected TSR:
@Stateless
public class ServiceBean implements Service {
@Resource
private TransactionSynchronizationRegistry tsr;
public void serviceInvocation() {
long timeMillis = (Long)tsr.getResource(KEY);
//...
System.out.println("Content is " + timeMillis);
}
}
TransactionSynchronizationRegistry works (should work) even in case you had assigned different thread pools to EJBs, which participate in the same transaction. The state would get lost with a simple ThreadLocal.
Because we are already in the lightweight Java EE 5 / 6 world - XML and other configuration plumbing are fully optional :-).
A deployable, working example (ContextHolder) was tested with Glassfish v3 and NetBeans 6.8m2 and pushed into http://kenai.com/projects/javaee-patterns/.
[See Context Holder pattern, page 247 in "Real World Java EE Patterns Rethinking Best Practices" book for more in-depth discussion]
Posted at 10:31AM Oct 14, 2009 by Adam Bien in Real World Java EE Patterns - Rethinking Best Practices | Comments[14] | Views/Hits: 2646
*NEW* Workshop: "Real World Java EE 6/7 Bootstrap" and book: Real World Java EE Night Hacks--Dissecting the Business Tier Tweet Follow @AdamBien



hi adam!
This seems not to work on WebSphere...
regards gustav
Posted by gustav on October 15, 2009 at 10:24 AM CEST #
dependency injection of TransactionSynchronizationRegistry fails in Interceptor and Entity-Listener.
JNDI Lookup works.
regards gustav
Posted by gustav on October 15, 2009 at 10:27 AM CEST #
But "KEY" itself is also context, which also needs to be passed around, so there is always something that you need to pass either via the callstack or via a thread-local kind of thing, no?
(Unless it's global - but then, the context itself becomes global, and passing data this way goes somewhat against information hiding).
Posted by Dimitris Andreou on October 15, 2009 at 11:49 AM CEST #
Hi Adam,
sounds cool and seems to work, so we might get rid of our ThreadLocal dependecies! How the **** did you find this class?
Thanks,
Norbert
@Dimitris: The key is local to a transaction. When using the same key within concurrent transactions it will reference different values.
Posted by Norbert Seekircher on October 15, 2009 at 05:19 PM CEST #
@Gustav,
1. "This seems not to work on WebSphere..." - then you will have to open an issue. It is a part of the spec.
2. "dependency injection of TransactionSynchronizationRegistry fails in Interceptor and Entity-Listener.
JNDI Lookup works." Works as designed - you cannot inject anything into JPA. A JNDI-lookup should work.
thanks for your feedback!,
adam
Posted by Adam Bien on October 15, 2009 at 09:10 PM CEST #
@Dimitris,
the key is just a constant. The value of the key is local to a transaction. Information hiding between layers is another issue. This technique is often used to pass additional information like security information or handle to a transaction-specific resource...
thanks!,
adam
Posted by Adam Bien on October 15, 2009 at 09:13 PM CEST #
@Norbert,
"How the **** did you find this class?"
A customer asked me during a review about my opinion about this approach :-). I found that in J2EE 1.4 spec and it worked well even at that time.
With Java EE 5 it not only works, but is really nice!,
thanks for your feedback,
adam
Posted by Adam Bien on October 15, 2009 at 09:14 PM CEST #
Annotation @Resource don`t work in jboss. The TransactionSynchronizationRegistry isn't made available in JNDI.
http://community.jboss.org/message/297351
Posted by Sergey Kiselev on May 17, 2010 at 05:00 AM CEST #
Sergey,
Actually you can make this work on JBoss if you do:
@javax.annotation.Resource( mappedName = "java:comp/TransactionSynchronizationRegistry" )
private TransactionSynchronizationRegistry mRegistry;
Hope that helps!
Posted by Richard Kennard on August 18, 2010 at 05:41 AM CEST #
I can achieve the same thing with a requestscoped bean (javax.enterprise.context.RequestScoped).
example :
1. Counter service
@Stateless
public class CounterService {
@Inject
Context context;
public int getCount(){
return context.getCount();
}
}
2. The counter is stored in the 'context' class
@RequestScoped
public class Context {
private int count=0;
public int getCount() {
return ++count;
}
With this, I can inject my context in every bean that need to access/update the context, and the context is scoped to the request that initiated the call, whatever it is a webapp, a web service or a mdb.
What is the benefit to use the TSR instead of a specific context bean ?
Is there a difference between using the TSR and using a requestscoped bean ?
Am I wrong somewhere ?
Thanks,
Nicolas
Posted by Nicolas NOEL on December 21, 2011 at 02:51 PM CET #
does EJBContext.getContextData work the same way with respect to thread safety?
Posted by Bill Schneider on December 28, 2011 at 05:51 PM CET #
@Nicolas
You're almost right but the request scope in CDI doesn't extend to some places you might be using in your app like:
* org.hibernate.Interceptor implementations,
* old EJB interceptors which are called before CDI creates the request scope
Tomasz
Posted by Tomasz Nikiel on January 03, 2012 at 05:51 PM CET #
@Nicolas,
you are almost right. You can achieve exactly the same if you have a JSF / HTTP frontend. If you are coming through a IIOP or JCA channel, you will have to use TSR or ThreadLocal for the context/request binding.
thanks for your constructive comment!,
adam
Posted by Adam Bien on January 04, 2012 at 04:53 PM CET #
@Adam
Yes, we were using remote calls through IIOP. Anyway, I have one more observation: the state cannot be conveyed to org.hibernate.Interceptor with ThreadLocal because the interceptor methods are called back in a different thread. Only TSR can do here and that's the approach we had to take.
To recap everything:
1. If you use JSF/HTTP frontend the CDI request scope is created early enough to be available through all EJB calls. The org.hibernate.Interceptor, however, doesn't support injection.
2. If you use IIOP (remote business interface calls) or JCA channel the CDI request scope is created after classical EJB interceptor calls.
3. In either case 1. or 2. you cannot inject a @RequestScoped object into the org.hibernate.Interceptor because it doesn't support injection.
4. In case 2. you could theoretically use ThreadLocal to pass state but it's not conveyed to org.hibernate.Interceptor, whose methods are called in a different thread.
5. In case 2. TSR is the way to go as it's available in all the stages.
Posted by Tomasz Nikiel on January 05, 2012 at 04:18 PM CET #