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]


NEW online workshop: Web Components training (online)

Airport MUC workshops: Java EE 8, Java 9: Bootstrap, Effective, Architectures, Single Page Apps, Progressive Web Apps, HTML 5, ES 6, CSS 3 and Microservices

Podcast: airhacks.fm and newsletter: airhacks.news

A book about rethinking Java EE Patterns

Comments:

hi adam!

This seems not to work on WebSphere...

regards gustav

Posted by gustav on October 15, 2009 at 12:24 PM CEST #

dependency injection of TransactionSynchronizationRegistry fails in Interceptor and Entity-Listener.
JNDI Lookup works.

regards gustav

Posted by gustav on October 15, 2009 at 12:27 PM 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 01:49 PM 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 07: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 11: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 11: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 11: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 07: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 07: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 03:51 PM CET #

does EJBContext.getContextData work the same way with respect to thread safety?

Posted by Bill Schneider on December 28, 2011 at 06: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 06: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 05: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 05:18 PM CET #

Thank you ;)

Posted by Manfred Hoess on May 23, 2012 at 05:02 PM CEST #

I kind of know that this is late but :

@Manfred

ad 1 and 3: It doesn't support injection, but supports retrieving the beanmanager thru jndi (from which you can get requested bean)

ad 4: we do use Thread local right now to do exactly this (didn't know about TSR when implemented this - I will probably refactor it.)

Posted by psychollek on December 19, 2014 at 10:40 AM CET #

Hi,

I was looking at the Arunja implementation of TransactionSynchronizationRegistry in JBoss and get/putResource works by accessing the resource from the transaction object. You state that this mechanism is independent of TLS, but how does the container get the TSR in the first place, so that it can inject it, if it doesn't do it using TLS?

Thanks,
Ant

Posted by Ant Kutschera on December 31, 2014 at 03:14 PM CET #

@Ant,

a good question, I'm going to cover it at the next: http://airhacks.tv,

thanks for asking and commenting!

cheers,

adam

Posted by Adam Bien on January 11, 2015 at 08:05 PM CET #

I've tried the @RequestScoped approach. Unfortunately the Scope isn't propagated to @Asynchronous methods: http://stackoverflow.com/questions/18658750/how-to-access-inject-a-sessionscoped-bean-from-an-ejbs-asynchronous-method :(

Posted by Bastian on January 21, 2015 at 10:47 AM CET #

I have the problems that with wildfly 8.2 it seems the TSR is not propagated to @asynchronous calls..

So when I call from one Bean a method on another EJB, the TSR contains the same data in both.. if I call from the same Bean a @asynchronous method, in that method the TSR is empty. Both Beans require Transactions. Any idea?

Posted by Peter Sellers on January 23, 2015 at 07:20 PM CET #

Wow, it looks like ultimate way to propagate user's language to remote EJB calls (and retrieve it in MessageInterpolator).

Posted by PeterW on March 03, 2015 at 08:36 PM CET #

@Bastian @Peter Sellers
I think @Asynchronous methods by their nature don't take place in the current transaction. Thats why there cannot be any state propagated to them via TSR.

Posted by Heli on May 14, 2015 at 09:01 PM CEST #

Hi Adam,

recently (by accident) i stumbled upon the standard CDI scope called @TransactionScoped: http://docs.oracle.com/javaee/7/api/javax/transaction/TransactionScoped.html
This seems to be available since JEE7 but isn't even mentioned in the official JEE Tutorial. Maybe you want to elaborate on this on your next airhacks.tv as an alternative to using TSR directly? Think it's worth spreading...
Great show by the way :-)

Br,
Heli

Posted by Heli on May 18, 2015 at 10:48 PM CEST #

Hi Adam, still a possibility limited to strings and the vm: System.get / setProperty () with the thread ID as key.

Posted by Gerd Tuschke on June 24, 2016 at 07:32 PM CEST #

Hi Adam,

Is this approach also supposed to work for remote EJB calls to remote VM over IIOP? I can't get that working (WebSphere 8.5).

Posted by Jimmy Praet on December 19, 2017 at 12:14 PM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
realworldpatterns.com
Online Workshops
...the last 150 posts
...the last 10 comments
License