1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.myfaces.trinidad.util;
20
21 import java.lang.ref.WeakReference;
22
23 import java.util.Collection;
24 import java.util.Iterator;
25
26 import java.util.List;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.concurrent.ConcurrentLinkedQueue;
30
31 /**
32 * Utility functions related to ThreadLocals.
33 * This class provides utilities for managing the lifecycles of ThreadLocals.
34 * In particular, many application servers do not clean up the ThreadLocals
35 * added to the request thread before returning the request thread to
36 * its thread pool. This may have several severe side-effects, including
37 * <ol>
38 * <li>Leakage of objects referenced by the thread local</li>
39 * <li>Possible security holes if the ThreadLocals contents are reused
40 * on a request for a different user.</li>
41 * <li>Leakage of the entire class loader in live patching scenarios</li>
42 * <li>ClassCastExceptions in live patching scenarios</li>
43 * </ol>
44 * To avoid these problems, this class provides utilities for creating
45 * and registering ThreadLocals that will be removed from the thread
46 * automatically at the end of the current request.
47 * @see #newRequestThreadLocal
48 * @see #registerRequestThreadLocal
49 */
50 public final class ThreadLocalUtils
51 {
52 /**
53 * Integration interface implemented by object holding onto ThreadLocals
54 * with a specified lifetime
55 * @see ThreadLocalLifecycle
56 */
57 public static abstract class ThreadLocalManager
58 {
59 /**
60 * Called by the ThreadLocalLifeCycle when the ThreadLocals managed by
61 * this ThreadLocalManager need to be cleaned up by removing their
62 * instances from the current Thread.
63 */
64 public abstract void removeThreadLocals();
65 }
66
67 /**
68 * Integration interface implemented by an object that cleans up the
69 * ThreadLocals in a ThreadLocalManager with the specified lifetime.
70 * The ThreadLocalLifecycle cleans up the ThreadLocals by calling
71 * <code>removeThreadLocals</code> on the ThreadLocalManager passed
72 * to it in <code>init</code> at the appropriate time.
73 * @see ThreadLocalManager
74 * @see #init
75 */
76 public static abstract class ThreadLocalLifecycle
77 {
78 /**
79 * Called after instantiating the ThreadLocalLifecycle so that the
80 * ThreadLocalLifecycle can clean up the ThreadLocalManager at the
81 * appropriate moment.
82 * @param manager Manager of ThreadLocals that will be cleaned up by
83 * this ThreadLocalLifecycle.
84 * @see ThreadLocalManager#removeThreadLocals
85 */
86 public abstract void init(ThreadLocalManager manager);
87 }
88
89 // don't allow instantiation
90 private ThreadLocalUtils()
91 {
92 }
93
94 /**
95 * Creates and returns a new ThreadLocal that will be automatically removed from
96 * the each request thread when the request finishes.
97 * @return The ThreadLocal
98 * @see #registerRequestThreadLocal
99 */
100 public static <T> ThreadLocal<T> newRequestThreadLocal()
101 {
102 return registerRequestThreadLocal(new ThreadLocal<T>());
103 }
104
105 /**
106 * Registers and returns the ThreadLocal to be automatically removed from
107 * the each request thread when the request finishes.
108 * @param threadLocal ThreadLocal to register for automatic removal
109 * @return The reigistered ThreadLocal
110 * @see #newRequestThreadLocal
111 */
112 public static <T> ThreadLocal<T> registerRequestThreadLocal(ThreadLocal<T> threadLocal)
113 {
114 return _getThreadLocalManager(_REQUEST_SERVICE).registerThreadLocal(threadLocal);
115 }
116
117 /**
118 * Returns the ResettableThreadLocalManager for the specified threadLocalGroup name. If no
119 * ResettableThreadLocalManager is currently registered for the threadLocalGroup, one will
120 * be instantiated, its related ThreadLocalLifecycle instantiated and the ThreadLocalManager
121 * registered with the ThreadLocalLifecycle
122 * @param threadLocalGroup Identifier for this group
123 * @return the ResettableThreadLocalManager for this threadLocalGroup name
124 */
125 private static ResettableThreadLocalManager _getThreadLocalManager(String threadLocalGroup)
126 {
127 // see if we already have a ResettableThreadLocalManager registered
128 ResettableThreadLocalManager threadLocalManager = _threadLocalManagers.get(threadLocalGroup);
129
130 // if we don't, create one
131 if (threadLocalManager == null)
132 {
133 threadLocalManager = new ResettableThreadLocalManager();
134
135 // handle simultaneous registration
136 ResettableThreadLocalManager oldThreadLocalManager =
137 _threadLocalManagers.putIfAbsent(threadLocalGroup, threadLocalManager);
138
139 if (oldThreadLocalManager != null)
140 {
141 // simultaneous registration, use the one that was registered first
142 threadLocalManager = oldThreadLocalManager;
143 }
144 else
145 {
146 // find the ThreadLocalLifecycle for this threadLocalGroup by looking in
147 // META-INF/Services, instantiate it and register ourselves with it so that we
148 // can be cleaned up
149 List<ThreadLocalLifecycle> lifecycles = ClassLoaderUtils.getServices(threadLocalGroup);
150
151 if (!lifecycles.isEmpty())
152 {
153 lifecycles.get(0).init(threadLocalManager);
154 }
155 }
156 }
157
158 return threadLocalManager;
159 }
160
161 /**
162 * ThreadLocalManager implementation class
163 */
164 private static class ResettableThreadLocalManager extends ThreadLocalManager
165 {
166 public ResettableThreadLocalManager()
167 {
168 // create the list of resettable ThreadLocals for this group
169 _threadLocals = new ConcurrentLinkedQueue<WeakReference<ThreadLocal<?>>>();
170 }
171
172 /**
173 * Registers the ThreadLocal for cleanup when this ThreadLocalManager is cleaned up
174 * @param threadLocal ThreadLocal to register for clean up
175 * @return The registered ThreadLocal
176 */
177 public <T> ThreadLocal<T> registerThreadLocal(ThreadLocal<T> threadLocal)
178 {
179 if (threadLocal == null)
180 throw new NullPointerException();
181
182 // make sure we don't pin ThreadLocals, in case they were dynamically created, or
183 // statically created on a class that was then unloaded
184 _threadLocals.add(new WeakReference<ThreadLocal<?>>(threadLocal));
185
186 return threadLocal;
187 }
188
189 /**
190 * Called by the ThreadLocalLifecycle to clean up all of the ThreadLocals registered
191 * with this ThreadLocalManager
192 */
193 public void removeThreadLocals()
194 {
195 Iterator<WeakReference<ThreadLocal<?>>> iterator = _threadLocals.iterator();
196
197 while (iterator.hasNext())
198 {
199 ThreadLocal<?> threadLocal = iterator.next().get();
200
201 if (threadLocal != null)
202 {
203 // clean up the ThreadLocal
204 threadLocal.remove();
205 }
206 else
207 {
208 // ThreadLocal was gc'ed, so remove the entry
209 iterator.remove();
210 }
211 }
212 }
213
214 private final Collection<WeakReference<ThreadLocal<?>>> _threadLocals;
215 }
216
217 // Name used for the request-scoped ThreadLocalManager
218 private static final String _REQUEST_SERVICE = ThreadLocalUtils.class.getName() +
219 ".ThreadLocalLifecycle.REQUEST";
220
221 // threadLocalGroup -> ThreadLocalManager Map
222 private static final ConcurrentMap<String, ResettableThreadLocalManager>_threadLocalManagers
223 = new ConcurrentHashMap<String, ResettableThreadLocalManager>();
224 }