performance optimization.
[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 is used for instance to pass a transaction scoped entity manager around.
28  * 
29  * The {@link #set(Object)} method sets the current service instance for the current thread. 
30  * The {@link #get()} method gets the current service instance for the current thread. 
31  * The {@link #getProxy()} method gets a proxy that will delegate at runtime to the thread-specific 
32  * instance. The result from this method can be passed at construction of an object that will be used
33  * by multiple threads. 
34  * 
35  * This class is mostly used by other test tools. 
36  * 
37  * @param T
38  *            Interface to proxy.
39  * @author Erik Brakkee
40  * 
41  */
42 public class ThreadSpecificProxyFactory<T> {
43     private class ThreadSpecificInvocationHandler implements InvocationHandler {
44
45         @Override
46         public Object invoke(Object aProxy, Method aMethod, Object[] aArgs)
47             throws Throwable {
48             try {
49                 return aMethod.invoke(svc.get(), aArgs);
50             } catch (InvocationTargetException e) {
51                 throw e.getCause();
52             }
53         }
54     }
55
56     private ThreadLocal<T> svc;
57     private Class clazz;
58     private T proxy; 
59
60     /**
61      * Constructs the factory.
62      * 
63      * @param aClass
64      *            Interface class of the service to proxy.
65      */
66     public ThreadSpecificProxyFactory(Class<T> aClass) {
67         if (!aClass.isInterface()) {
68             throw new IllegalArgumentException("Class " + aClass.getName() +
69                 " is not an interface");
70         }
71         svc = new ThreadLocal<T>();
72         clazz = aClass;
73         proxy = createProxy(); 
74     }
75
76     /**
77      * Sets the thread-specific service.
78      * 
79      * @param aService
80      *            Service, use null value to reset.
81      */
82     public void set(T aService) {
83         svc.set(aService);
84     }
85     
86     /**
87      * Gets the current thread-specific service.
88      * To get a contextual reference that can be used by any thread but delegates to a thread-specific
89      * instance, use {@link #getProxy()}.  
90      * @return Service. 
91      */
92     public T get() { 
93         return svc.get();
94     }
95
96     /**
97      * Gets the proxy that delegates to the thread-specific instance set by
98      * {@link #set(Object)}
99      * 
100      * @return Proxy.
101      */
102     public T getProxy() {
103         return proxy;
104     }
105
106     private T createProxy() {
107         InvocationHandler handler = new ThreadSpecificInvocationHandler();
108         Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(),
109             new Class[] { clazz });
110         T proxyObj;
111         try {
112             proxyObj = (T) proxyClass.getConstructor(
113                 new Class[] { InvocationHandler.class }).newInstance(
114                 new Object[] { handler });
115             return proxyObj;
116         } catch (Exception e) {
117             throw new RuntimeException("Could not create proxy for " +
118                 clazz.getName(), e);
119         }
120     }
121 }