0

In a traditional setting, my app does something simple: Accept a request, load some metadata (from a heavy restful call) pertaining to the particular user who sent in that request and store this metadata in a threadlocal bean context (i.e. in a simple hashMap backed into the context's class) so that I can painlessly access these details from any other class in the code path as long as I inject the bean. This works harmoniously with all the other non-threadlocal beans that are injected across my API's lifecycle so there aren't any issues.

However, once I try to do some clever things (kick off some task in a separate thread to mimic a "fire and forget" sort of call) I noticed that wherever the code path (in this new thread) relies on the threadlocal bean I get burned because the details that the hashMap (baked into the context's class) previously held have now disappeared. It makes perfect sense to me why they've evaporated because after all, once I kicked off a new thread, it hasn't "cloned" the parent thread's context into the new child thread's context automatically.

My question is that is there any existing spring framework mechanism (or plain Java for that matter) that I can leverage to make sure that I preserve this information for the new child thread that I fire off? How trivial is it to "deepClone" the original parent thread's context into the new child? If that's a naïve way to do it, can someone recommend some other set up i can engage in before kicking off the thread? Given that I'm looking to maintain this context only for the request's scope I'm unsure if I can use something like Spring's object pooling (after all, I don't want a situation where a new request comes in and a recycle a different/old user's preferences for the new one).

Community
  • 1
  • 1
BSJ
  • 1,185
  • 2
  • 10
  • 15

1 Answers1

2

I see three ways you could work.

Use ’InheritableThreadLocal`

This is a subclass of ThreadLocal and a default JDK feature. Inheritable thread locals are automatically copied in children threads from their parent's. Seems Like a good candidate.
Edited : if your code looks something like this, then it's a drop-in replacement.

public class Test {

// I guess this, in your actual code, is an injected Reference
InheritableThreadLocal<String> expensiveData = new InheritableThreadLocal<String>();

public void work(String userName){
    expensiveData.set(computeExpensiveData(userName)); // whatever that is

    Worker workUnit = new Worker();
    workUnit.userData = expensiveData; // Spring does some variant of this (I guess)

    Thread child = new Thread(workUnit); // This is the key line
    child.start(); // As long as the parent thread has a clean "expensiveData"
                   // The child thread will have too.
}

protected static class Worker implements Runnable {
    // This is injected too...
    protected InheritableThreadLocal<String> userData = null;
    @Override
    public void run() {
        userData.get(); // Returns spwaning thread's version
        // Work ...!
        return;
    }
}
}

There is an actual risk of having a bad value coming out of Worker's userData if the Thread instances (the child variable here) are not created or started here, but reused in a pool.

Use a request/session scoped bean

If the wayy you access your datas is through some kind of bean, maybe you could tie this bean's lifecycle to your webapp's session or request. You'd get a new bean automatically each time, but you'd keep the same one inside your scope.

Refactor as a cache

Spring has @Cachable annotations that allow reasonnably easy caching of given method calls. Once again, You could make the access to your data be @Cachable, and tune the cache configuration to be "per user".
Edit : Spring cache uses proxying, so it's a metod-leve interception that is at work. I call this a reafactor because it will need some work on you part, but this is in my opinion a cleaner, more spring-like way of doint things. Starting from the same example as above, the key difference is that you'd have to create a new Bean, that would implement :

public static interface ExpensiveUserDataCalculator {
    @Cacheable // With the proper configuration tuning (eviction ? TTL ? size ?)
    public String computeExpensiveData(String userName);
}

The implementation is just a detail (you'd probably copy/paste it from your current code). This bean would be injected (it's a collaborator in Spring's lingo).

This would look like this. Note that you do not have to manipulate ThreadLocal or spawn threads or whatever. Just have each collaborator do its job, wire them together with Spring, then optimise with @Cacheable (or other mean of caching as you see fit). Caching then is an "implementation detail" of your workflow.

// This is a thread pool, or whatever...
protected Object workDispatcher = null;

public void work(String userName){
    Worker workUnit = new Worker(userName); // Setup
    workDispatcher.dispatch(workUnit);
}

protected static class Worker implements Runnable {
    // This is injected Injected in you situation (?)
    protected ExpensiveUserDataCalculator userDataProvider = null;
    protected String userName = null;
    public Worker(String userName) { this.userName = userName; }
    @Override
    public void run() {
        userDataProvider.computeExpensiveData(userName); 
        // Work ...!
        return;
    }
}

The last two approches have their own section in the Spring Reference Guide, so check it out if need be.

GPI
  • 9,088
  • 2
  • 31
  • 38
  • Just to clarify, wont using InheritableThreadLocal have the threads recycled in the threadpool which would lead to information leakage? As for the cachable annotation, instead of making a method call cacheable, is it possible for me to make a class's attributes cacheable (so in my example, i'll just set 5 different attributes one of which relies on the method call)? – BSJ Jul 26 '14 at 02:01