Now the thread-specific proxies are serializable.
authorerik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Wed, 21 Jul 2010 12:12:04 +0000 (12:12 +0000)
committererik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Wed, 21 Jul 2010 12:12:04 +0000 (12:12 +0000)
support/general/src/main/java/org/wamblee/general/ThreadSpecificProxyFactory.java
support/general/src/test/java/org/wamblee/general/ThreadSpecificProxyFactoryTest.java

index a5a1cebb3871e8b3334c3ecaabed71fa6232fab3..036a6828648b2cd25153d8ecb6edcd8c517b7e73 100644 (file)
  */
 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
@@ -41,13 +45,36 @@ import java.lang.reflect.Proxy;
  * 
  */
 public class ThreadSpecificProxyFactory<T> {
-    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<String,ThreadLocal> STORAGE = 
+        initializeThreadLocal();
+    
+    private static class ThreadSpecificInvocationHandler<T> 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<T> 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();
             }
@@ -74,6 +101,11 @@ public class ThreadSpecificProxyFactory<T> {
         proxy = createProxy(); 
     }
 
+    private static Map<String, ThreadLocal> initializeThreadLocal() {
+        Map<String,ThreadLocal> map = new HashMap<String,ThreadLocal>();
+        return map;
+    }
+
     /**
      * Sets the thread-specific service.
      * 
@@ -105,7 +137,9 @@ public class ThreadSpecificProxyFactory<T> {
     }
 
     private T createProxy() {
-        InvocationHandler handler = new ThreadSpecificInvocationHandler();
+        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 proxyObj;
index 310e47a75626aaf14bcd031aefb7c16ef8466bd5..9b52817e5251bb241437cd6b685f428b86cee52a 100644 (file)
@@ -104,4 +104,9 @@ public class ThreadSpecificProxyFactoryTest {
     public void testNotAnInterface() {
         ThreadSpecificProxyFactory f = new ThreadSpecificProxyFactory(String.class);
     }
+    
+    @Test
+    public void testProxyToStringWhileUninitialized() { 
+        String val = proxy.toString();
+    }
 }