X-Git-Url: http://wamblee.org/gitweb/?a=blobdiff_plain;f=support%2Fgeneral%2Fsrc%2Fmain%2Fjava%2Forg%2Fwamblee%2Fgeneral%2FThreadSpecificProxyFactory.java;h=036a6828648b2cd25153d8ecb6edcd8c517b7e73;hb=a09ef498cd7b52e5c77c2fbfcd26203eca50d643;hp=f7087296796d8a60ae14fc83139aed7603c5ef2e;hpb=86b68ca4594c43d09ca57f8a2f9522c604c76f1d;p=utils diff --git a/support/general/src/main/java/org/wamblee/general/ThreadSpecificProxyFactory.java b/support/general/src/main/java/org/wamblee/general/ThreadSpecificProxyFactory.java index f7087296..036a6828 100644 --- a/support/general/src/main/java/org/wamblee/general/ThreadSpecificProxyFactory.java +++ b/support/general/src/main/java/org/wamblee/general/ThreadSpecificProxyFactory.java @@ -15,16 +15,21 @@ */ package org.wamblee.general; +import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; /** * Thread-specific proxy is used to create implementations of interfaces that * delegate to a thread-specific implementation of the service. * - * It is used for instance to pass a transaction scoped entity manager around. + * It can be used for instance to create a contextual reference to an entity manager + * that delegates to a thread-specific instance. * * The {@link #set(Object)} method sets the current service instance for the current thread. * The {@link #get()} method gets the current service instance for the current thread. @@ -32,7 +37,7 @@ import java.lang.reflect.Proxy; * instance. The result from this method can be passed at construction of an object that will be used * by multiple threads. * - * This class is mostly used by other test tools. + * This class is mostly used by infrastructure code (utilities) and test tools. * * @param T * Interface to proxy. @@ -40,21 +45,45 @@ import java.lang.reflect.Proxy; * */ public class ThreadSpecificProxyFactory { - private class ThreadSpecificInvocationHandler implements InvocationHandler { + + /** + * We store a map of unique ids of invocation handlers to thread local storage of the + * service. In this way, serialiability of the generated proxy is obtained (required by + * framweorks such as wicket). Also, different factories will still be separate and never + * use the same threadlocal storage. + */ + private static Map STORAGE = + initializeThreadLocal(); + + private static class ThreadSpecificInvocationHandler implements InvocationHandler, Serializable { + + private String id; + private Class clazz; + + public ThreadSpecificInvocationHandler(String aId, Class aClass) { + id = aId; + clazz = aClass; + } @Override public Object invoke(Object aProxy, Method aMethod, Object[] aArgs) throws Throwable { + ThreadLocal local = STORAGE.get(id); + T actualSvc = local.get(); + if ( aMethod.getName().equals("toString") && actualSvc == null) { + return "Thread-specific proxy for '" + clazz.getName() + "'"; + } try { - return aMethod.invoke(svc.get(), aArgs); + return aMethod.invoke(actualSvc, aArgs); } catch (InvocationTargetException e) { throw e.getCause(); } } } - private ThreadLocal svc = new ThreadLocal(); + private ThreadLocal svc; private Class clazz; + private T proxy; /** * Constructs the factory. @@ -67,7 +96,14 @@ public class ThreadSpecificProxyFactory { throw new IllegalArgumentException("Class " + aClass.getName() + " is not an interface"); } + svc = new ThreadLocal(); clazz = aClass; + proxy = createProxy(); + } + + private static Map initializeThreadLocal() { + Map map = new HashMap(); + return map; } /** @@ -81,7 +117,9 @@ public class ThreadSpecificProxyFactory { } /** - * Gets the current thread-specific service. + * Gets the current thread-specific service. + * To get a contextual reference that can be used by any thread but delegates to a thread-specific + * instance, use {@link #getProxy()}. * @return Service. */ public T get() { @@ -95,15 +133,21 @@ public class ThreadSpecificProxyFactory { * @return Proxy. */ public T getProxy() { - InvocationHandler handler = new ThreadSpecificInvocationHandler(); + return proxy; + } + + private T createProxy() { + String id = UUID.randomUUID().toString(); + STORAGE.put(id, svc); + InvocationHandler handler = new ThreadSpecificInvocationHandler(id, clazz); Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), new Class[] { clazz }); - T proxy; + T proxyObj; try { - proxy = (T) proxyClass.getConstructor( + proxyObj = (T) proxyClass.getConstructor( new Class[] { InvocationHandler.class }).newInstance( new Object[] { handler }); - return proxy; + return proxyObj; } catch (Exception e) { throw new RuntimeException("Could not create proxy for " + clazz.getName(), e);