e3b687df15777ac1fd07003c9d04ed272b925d66
[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.Proxy;
20
21 /**
22  * Thread-specific proxy is used to create implementations of interfaces that
23  * delegate to a thread-specific implementation of the service.
24  * 
25  * It can be used for instance to create a contextual reference to an entity manager 
26  * that delegates to a thread-specific instance. 
27  * 
28  * The {@link #set(Object)} method sets the current service instance for the current thread. 
29  * The {@link #get()} method gets the current service instance for the current thread. 
30  * The {@link #getProxy()} method gets a proxy that will delegate at runtime to the thread-specific 
31  * instance. The result from this method can be passed at construction of an object that will be used
32  * by multiple threads. 
33  * 
34  * This class is mostly used by infrastructure code (utilities) and test tools.
35  * 
36  * Care has been taken so that the invocation handler is serializable.
37  * However, it is only serializable within one virtual machine. It cannot be used in a distributed context
38  * where it can be sent to another JVM. 
39  * 
40  * @param T
41  *            Interface to proxy.
42  * @author Erik Brakkee
43  * 
44  */
45 public class ThreadSpecificProxyFactory<T> {
46    
47     
48     private ThreadLocal<T> svc;
49     private Class clazz;
50     private T proxy; 
51
52     /**
53      * Constructs the factory.
54      * 
55      * @param aClass
56      *            Interface class of the service to proxy.
57      */
58     public ThreadSpecificProxyFactory(Class<T> aClass) {
59         if (!aClass.isInterface()) {
60             throw new IllegalArgumentException("Class " + aClass.getName() +
61                 " is not an interface");
62         }
63         svc = new ThreadLocal<T>();
64         clazz = aClass;
65         proxy = createProxy(); 
66         
67     }
68
69     /**
70      * Sets the thread-specific service.
71      * 
72      * @param aService
73      *            Service, use null value to reset.
74      */
75     public void set(T aService) {
76         svc.set(aService);
77     }
78     
79     /**
80      * Gets the current thread-specific service.
81      * To get a contextual reference that can be used by any thread but delegates to a thread-specific
82      * instance, use {@link #getProxy()}.  
83      * @return Service. 
84      */
85     public T get() { 
86         return svc.get();
87     }
88
89     /**
90      * Gets the proxy that delegates to the thread-specific instance set by
91      * {@link #set(Object)}
92      * 
93      * @return Proxy.
94      */
95     public T getProxy() {
96         return proxy;
97     }
98
99     private T createProxy() {
100         InvocationHandler handler = new ThreadSpecificInvocationHandler(svc, clazz);
101         Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(),
102             new Class[] { clazz });
103         T proxyObj;
104         try {
105             proxyObj = (T) proxyClass.getConstructor(
106                 new Class[] { InvocationHandler.class }).newInstance(
107                 new Object[] { handler });
108             return proxyObj;
109         } catch (Exception e) {
110             throw new RuntimeException("Could not create proxy for " +
111                 clazz.getName(), e);
112         }
113     }
114 }