As a Lispper, I very much agree with your worldview and will consider it a shame if you reconsider it. :-)
If it were me, I would simply use ThreadGroup
for each top-level process and associate each connection with the group in which the caller works. If you use with thread pools, just make sure that the pools use threads in the correct thread group (for example, having a pool in each thread group).
Implementation Example:
public class CachedConnection { } public class ProcessContext extends ThreadGroup { private static final Map<ProcessContext, Map<Class, Object>> contexts = new WeakHashMap<ProcessContext, Map<Class, Object>>(); public static T getContext(Class<T> cls) { ProcessContext tg = currentContext(); Map<Class, Object> ctx; synchronized(contexts) { if((ctx = contexts.get(tg)) == null) contexts.put(tg, ctx = new HashMap<Class, Object>()); } synchronized(ctx) { Object cur = ctx.get(cls); if(cur != null) return(cls.cast(cur)); T new_t; try { new_t = cls.newInstance(); } catch(Exception e) { throw(new RuntimeException(e)); } ctx.put(cls, new_t); return(new_t); } } public static ProcessContext currentContext() { ThreadGroup tg = Thread.currentThread().getThreadGroup(); while(true) { if(tg instanceof ProcessContext) return((ProcessContext)tg); tg = tg.getParent(); if(tg == null) throw(new IllegalStateException("Not running in a ProcessContext")); } } }
If you just simply run all your threads in the correct ProcessContext
, you can get CachedConnection
anywhere by calling ProcessContext.getContext(CachedConnection.class)
.
Of course, as mentioned above, you will need to make sure that any other threads you can delegate also work in the correct ProcessContext, but I'm sure the problem is inherent in your description - you obviously need to specify one of several ways contexts that your delegate staff have encountered. In any case, one could modify the ProcessContext as follows:
public class ProcessContext extends ThreadGroup { private static final ThreadLocal<ProcessContext> tempctx = new ThreadLocal<ProcessContext>(); public static ProcessContext currentContext() { if(tempctx.get() != null) return(tempctx.get()); ThreadGroup tg = Thread.currentThread().getThreadGroup(); while(true) { if(tg instanceof ProcessContext) return((ProcessContext)tg); tg = tg.getParent(); if(tg == null) throw(new IllegalStateException("Not running in a ProcessContext")); } } public class RunnableInContext implements Runnable { private final Runnable delegate; public RunnableInContext(Runnable delegate) {this.delegate = delegate;} public void run() { ProcessContext old = tempctx.get(); tempctx.set(ProcessContext.this); try { delegate.run(); } finally { tempctx.set(old); } } } public static Runnable wrapInContext(Runnable delegate) { return(currentContext().new RunnableInContext(delegate)); } }
Thus, you can use ProcessContext.wrapInContext()
to pass a Runnable
that, when launched, inherits its context from where it was created.
(Please note that I have not actually tried the above code, so it could very well be filled in typos.)
Dolda2000
source share