a5a1cebb3871e8b3334c3ecaabed71fa6232fab3
[utils] / support / general / src / main / java / org / wamblee / general / ThreadSpecificProxyFactory.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.general;
17
18 import java.lang.reflect.InvocationHandler;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Proxy;
22
23 /**
24  * Thread-specific proxy is used to create implementations of interfaces that
25  * delegate to a thread-specific implementation of the service.
26  * 
27  * It can be used for instance to create a contextual reference to an entity manager 
28  * that delegates to a thread-specific instance. 
29  * 
30  * The {@link #set(Object)} method sets the current service instance for the current thread. 
31  * The {@link #get()} method gets the current service instance for the current thread. 
32  * The {@link #getProxy()} method gets a proxy that will delegate at runtime to the thread-specific 
33  * instance. The result from this method can be passed at construction of an object that will be used
34  * by multiple threads. 
35  * 
36  * This class is mostly used by infrastructure code (utilities) and test tools.
37  * 
38  * @param T
39  *            Interface to proxy.
40  * @author Erik Brakkee
41  * 
42  */
43 public class ThreadSpecificProxyFactory<T> {
44     private class ThreadSpecificInvocationHandler implements InvocationHandler {
45
46         @Override
47         public Object invoke(Object aProxy, Method aMethod, Object[] aArgs)
48             throws Throwable {
49             try {
50                 return aMethod.invoke(svc.get(), aArgs);
51             } catch (InvocationTargetException e) {
52                 throw e.getCause();
53             }
54         }
55     }
56
57     private ThreadLocal<T> svc;
58     private Class clazz;
59     private T proxy; 
60
61     /**
62      * Constructs the factory.
63      * 
64      * @param aClass
65      *            Interface class of the service to proxy.
66      */
67     public ThreadSpecificProxyFactory(Class<T> aClass) {
68         if (!aClass.isInterface()) {
69             throw new IllegalArgumentException("Class " + aClass.getName() +
70                 " is not an interface");
71         }
72         svc = new ThreadLocal<T>();
73         clazz = aClass;
74         proxy = createProxy(); 
75     }
76
77     /**
78      * Sets the thread-specific service.
79      * 
80      * @param aService
81      *            Service, use null value to reset.
82      */
83     public void set(T aService) {
84         svc.set(aService);
85     }
86     
87     /**
88      * Gets the current thread-specific service.
89      * To get a contextual reference that can be used by any thread but delegates to a thread-specific
90      * instance, use {@link #getProxy()}.  
91      * @return Service. 
92      */
93     public T get() { 
94         return svc.get();
95     }
96
97     /**
98      * Gets the proxy that delegates to the thread-specific instance set by
99      * {@link #set(Object)}
100      * 
101      * @return Proxy.
102      */
103     public T getProxy() {
104         return proxy;
105     }
106
107     private T createProxy() {
108         InvocationHandler handler = new ThreadSpecificInvocationHandler();
109         Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(),
110             new Class[] { clazz });
111         T proxyObj;
112         try {
113             proxyObj = (T) proxyClass.getConstructor(
114                 new Class[] { InvocationHandler.class }).newInstance(
115                 new Object[] { handler });
116             return proxyObj;
117         } catch (Exception e) {
118             throw new RuntimeException("Could not create proxy for " +
119                 clazz.getName(), e);
120         }
121     }
122 }