X-Git-Url: http://wamblee.org/gitweb/?a=blobdiff_plain;f=support%2Fgeneral%2Fsrc%2Fmain%2Fjava%2Forg%2Fwamblee%2Fgeneral%2FThreadSpecificProxyFactory.java;h=81127009e4a6afc09993800789637a0001c78ce0;hb=66d09690a9c113766d63df4e7623534fb278f265;hp=036a6828648b2cd25153d8ecb6edcd8c517b7e73;hpb=a09ef498cd7b52e5c77c2fbfcd26203eca50d643;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 036a6828..81127009 100644 --- a/support/general/src/main/java/org/wamblee/general/ThreadSpecificProxyFactory.java +++ b/support/general/src/main/java/org/wamblee/general/ThreadSpecificProxyFactory.java @@ -15,29 +15,38 @@ */ 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 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. - * The {@link #getProxy()} method gets a proxy that will delegate at runtime to the thread-specific - * instance. The result from this method can be passed at construction of an object that will be used - * by multiple threads. + *

+ * 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. The {@link #getProxy()} method gets a proxy that will + * delegate at runtime to the thread-specific 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 infrastructure code (utilities) and test tools. + *

+ * + *

+ * Care has been taken so that the invocation handler is serializable. However, + * it is only serializable within one virtual machine. It cannot be used in a + * distributed context where it can be sent to another JVM. + *

* * @param T * Interface to proxy. @@ -45,45 +54,26 @@ import java.util.UUID; * */ public class ThreadSpecificProxyFactory { - + /** - * 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. + * Optional callback invoked to create the thread-specific object when there + * is no object yet associated with the current thread. + * + * @author Erik Brakkee + * */ - 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(actualSvc, aArgs); - } catch (InvocationTargetException e) { - throw e.getCause(); - } - } + public static interface CreationCallback { + /** + * Creates the object. + * + * @return Object. + */ + T create(); } private ThreadLocal svc; private Class clazz; - private T proxy; + private T proxy; /** * Constructs the factory. @@ -92,18 +82,37 @@ public class ThreadSpecificProxyFactory { * Interface class of the service to proxy. */ public ThreadSpecificProxyFactory(Class aClass) { + this(aClass, null); + } + + /** + * Constructs the factory with a callback to create thread-specific objects + * automatically. + * + * @param aClass + * Interface class of the service to proxy. + * @param aCallback + * Callback to create the object if it does not exist. When null, + * then no initialization is done. + */ + public ThreadSpecificProxyFactory(Class aClass, + final CreationCallback aCallback) { if (!aClass.isInterface()) { throw new IllegalArgumentException("Class " + aClass.getName() + " is not an interface"); } - svc = new ThreadLocal(); + svc = new ThreadLocal() { + @Override + protected T initialValue() { + if ( aCallback != null ) { + return aCallback.create(); + } + return null; + } + }; clazz = aClass; - proxy = createProxy(); - } + proxy = createProxy(); - private static Map initializeThreadLocal() { - Map map = new HashMap(); - return map; } /** @@ -115,14 +124,15 @@ public class ThreadSpecificProxyFactory { public void set(T aService) { svc.set(aService); } - + /** - * 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. + * 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() { + public T get() { return svc.get(); } @@ -137,9 +147,8 @@ public class ThreadSpecificProxyFactory { } private T createProxy() { - String id = UUID.randomUUID().toString(); - STORAGE.put(id, svc); - InvocationHandler handler = new ThreadSpecificInvocationHandler(id, clazz); + InvocationHandler handler = new ThreadSpecificInvocationHandler(svc, + clazz); Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), new Class[] { clazz }); T proxyObj;