From: Erik Brakkee Date: Fri, 23 Jul 2010 14:44:14 +0000 (+0000) Subject: (no commit message) X-Git-Tag: wamblee-utils-0.7~206 X-Git-Url: http://wamblee.org/gitweb/?a=commitdiff_plain;h=8d9d3071eebb9a14a1ebd44294d7f4c1acb41ac1;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 e3b687df..ce912ef3 100644 --- a/support/general/src/main/java/org/wamblee/general/ThreadSpecificProxyFactory.java +++ b/support/general/src/main/java/org/wamblee/general/ThreadSpecificProxyFactory.java @@ -22,20 +22,21 @@ import java.lang.reflect.Proxy; * 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. + * 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. + * 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. + * 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. @@ -43,11 +44,26 @@ import java.lang.reflect.Proxy; * */ public class ThreadSpecificProxyFactory { - - + + /** + * Optional callback invoked to create the thread-specific object when there + * is no object yet associated with the current thread. + * + * @author Erik Brakkee + * + */ + 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. @@ -56,14 +72,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(); + } /** @@ -75,14 +114,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(); } @@ -97,7 +137,8 @@ public class ThreadSpecificProxyFactory { } private T createProxy() { - InvocationHandler handler = new ThreadSpecificInvocationHandler(svc, clazz); + InvocationHandler handler = new ThreadSpecificInvocationHandler(svc, + clazz); Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), new Class[] { clazz }); T proxyObj; diff --git a/support/general/src/test/java/org/wamblee/general/ThreadSpecificProxyFactoryTest.java b/support/general/src/test/java/org/wamblee/general/ThreadSpecificProxyFactoryTest.java index 9b52817e..bbb39509 100644 --- a/support/general/src/test/java/org/wamblee/general/ThreadSpecificProxyFactoryTest.java +++ b/support/general/src/test/java/org/wamblee/general/ThreadSpecificProxyFactoryTest.java @@ -23,6 +23,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.wamblee.general.ThreadSpecificProxyFactory; +import org.wamblee.general.ThreadSpecificProxyFactory.CreationCallback; public class ThreadSpecificProxyFactoryTest { @@ -57,6 +58,20 @@ public class ThreadSpecificProxyFactoryTest { assertEquals(50, proxy.execute(10)); verify(svc).execute(10); } + + @Test + public void testCreationCallback() throws Exception { + CreationCallback callback = mock(CreationCallback.class); + Service initialService = mock(Service.class); + when(callback.create()).thenReturn(initialService); + + factory = new ThreadSpecificProxyFactory(Service.class, callback); + proxy = factory.getProxy(); + Service svc = factory.get(); + + assertSame(initialService, svc); + verify(callback).create(); + } @Test public void testInvokeThroughProxyWithException() throws Exception {