Support classes for testing with services that require
[utils] / test / enterprise / src / main / java / org / wamblee / support / 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.support;
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  * @param T
30  *            Interface to proxy.
31  * @author Erik Brakkee
32  * 
33  */
34 public class ThreadSpecificProxyFactory<T> {
35     private class ThreadSpecificInvocationHandler implements InvocationHandler {
36
37         @Override
38         public Object invoke(Object aProxy, Method aMethod, Object[] aArgs)
39             throws Throwable {
40             try {
41                 return aMethod.invoke(svc.get(), aArgs);
42             } catch (InvocationTargetException e) {
43                 throw e.getCause();
44             }
45         }
46     }
47
48     private ThreadLocal<T> svc = new ThreadLocal<T>();
49     private Class clazz;
50
51     /**
52      * Constructs the factory. 
53      * @param aClass Interface class of the service to proxy. 
54      */
55     public ThreadSpecificProxyFactory(Class<T> aClass) {
56         if ( !aClass.isInterface() ) { 
57             throw new IllegalArgumentException("Class " + aClass.getName() + " is not an interface");
58         }
59         clazz = aClass;
60     }
61
62     /**
63      * Sets the thread-specific service. 
64      * @param aService Service, use null value to reset.  
65      */
66     public void set(T aService) {
67         svc.set(aService);
68     }
69
70     /**
71      * Gets the proxy that delegates to the thread-specific instance set by 
72      * {@link #set(Object)}
73      * @return Proxy. 
74      */
75     public T getProxy() {
76         InvocationHandler handler = new ThreadSpecificInvocationHandler();
77         Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(),
78             new Class[] { clazz });
79         T proxy;
80         try {
81             proxy = (T) proxyClass.getConstructor(
82                 new Class[] { InvocationHandler.class }).newInstance(
83                 new Object[] { handler });
84             return proxy;
85         } catch (Exception e) { 
86             throw new RuntimeException("Could not create proxy for " + clazz.getName(), e);
87         }
88     }
89 }