(no commit message)
authorerik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Fri, 23 Jul 2010 14:44:14 +0000 (14:44 +0000)
committererik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Fri, 23 Jul 2010 14:44:14 +0000 (14:44 +0000)
support/general/src/main/java/org/wamblee/general/ThreadSpecificProxyFactory.java
support/general/src/test/java/org/wamblee/general/ThreadSpecificProxyFactoryTest.java

index e3b687df15777ac1fd07003c9d04ed272b925d66..ce912ef37b53b6c54dd8a51717f6cced462991e5 100644 (file)
@@ -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<T> {
-   
-    
+
+    /**
+     * 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<T> {
+        /**
+         * Creates the object.
+         * 
+         * @return Object.
+         */
+        T create();
+    }
+
     private ThreadLocal<T> svc;
     private Class clazz;
-    private T proxy; 
+    private T proxy;
 
     /**
      * Constructs the factory.
@@ -56,14 +72,37 @@ public class ThreadSpecificProxyFactory<T> {
      *            Interface class of the service to proxy.
      */
     public ThreadSpecificProxyFactory(Class<T> 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<T> aClass,
+        final CreationCallback<T> aCallback) {
         if (!aClass.isInterface()) {
             throw new IllegalArgumentException("Class " + aClass.getName() +
                 " is not an interface");
         }
-        svc = new ThreadLocal<T>();
+        svc = new ThreadLocal<T>() {
+            @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<T> {
     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<T> {
     }
 
     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;
index 9b52817e5251bb241437cd6b685f428b86cee52a..bbb395090f0a49351595d24a6284c208a9366be1 100644 (file)
@@ -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>(Service.class, callback);
+        proxy = factory.getProxy();
+        Service svc = factory.get();
+        
+        assertSame(initialService, svc); 
+        verify(callback).create();
+    }
 
     @Test
     public void testInvokeThroughProxyWithException() throws Exception {