2 * Copyright 2005-2010 the original author or authors.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.wamblee.general;
18 import java.io.Serializable;
19 import java.lang.reflect.InvocationHandler;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.lang.reflect.Proxy;
23 import java.util.HashMap;
25 import java.util.UUID;
28 * Thread-specific proxy is used to create implementations of interfaces that
29 * delegate to a thread-specific implementation of the service.
31 * It can be used for instance to create a contextual reference to an entity manager
32 * that delegates to a thread-specific instance.
34 * The {@link #set(Object)} method sets the current service instance for the current thread.
35 * The {@link #get()} method gets the current service instance for the current thread.
36 * The {@link #getProxy()} method gets a proxy that will delegate at runtime to the thread-specific
37 * instance. The result from this method can be passed at construction of an object that will be used
38 * by multiple threads.
40 * This class is mostly used by infrastructure code (utilities) and test tools.
44 * @author Erik Brakkee
47 public class ThreadSpecificProxyFactory<T> {
50 * We store a map of unique ids of invocation handlers to thread local storage of the
51 * service. In this way, serialiability of the generated proxy is obtained (required by
52 * framweorks such as wicket). Also, different factories will still be separate and never
53 * use the same threadlocal storage.
55 private static Map<String,ThreadLocal> STORAGE =
56 initializeThreadLocal();
58 private static class ThreadSpecificInvocationHandler<T> implements InvocationHandler, Serializable {
63 public ThreadSpecificInvocationHandler(String aId, Class aClass) {
69 public Object invoke(Object aProxy, Method aMethod, Object[] aArgs)
71 ThreadLocal<T> local = STORAGE.get(id);
72 T actualSvc = local.get();
73 if ( aMethod.getName().equals("toString") && actualSvc == null) {
74 return "Thread-specific proxy for '" + clazz.getName() + "'";
77 return aMethod.invoke(actualSvc, aArgs);
78 } catch (InvocationTargetException e) {
84 private ThreadLocal<T> svc;
89 * Constructs the factory.
92 * Interface class of the service to proxy.
94 public ThreadSpecificProxyFactory(Class<T> aClass) {
95 if (!aClass.isInterface()) {
96 throw new IllegalArgumentException("Class " + aClass.getName() +
97 " is not an interface");
99 svc = new ThreadLocal<T>();
101 proxy = createProxy();
104 private static Map<String, ThreadLocal> initializeThreadLocal() {
105 Map<String,ThreadLocal> map = new HashMap<String,ThreadLocal>();
110 * Sets the thread-specific service.
113 * Service, use null value to reset.
115 public void set(T aService) {
120 * Gets the current thread-specific service.
121 * To get a contextual reference that can be used by any thread but delegates to a thread-specific
122 * instance, use {@link #getProxy()}.
130 * Gets the proxy that delegates to the thread-specific instance set by
131 * {@link #set(Object)}
135 public T getProxy() {
139 private T createProxy() {
140 String id = UUID.randomUUID().toString();
141 STORAGE.put(id, svc);
142 InvocationHandler handler = new ThreadSpecificInvocationHandler(id, clazz);
143 Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(),
144 new Class[] { clazz });
147 proxyObj = (T) proxyClass.getConstructor(
148 new Class[] { InvocationHandler.class }).newInstance(
149 new Object[] { handler });
151 } catch (Exception e) {
152 throw new RuntimeException("Could not create proxy for " +