Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ThreadContext |
|
| 2.3125;2.312 | ||||
ThreadContext$1 |
|
| 2.3125;2.312 | ||||
ThreadContext$InheritableThreadLocalMap |
|
| 2.3125;2.312 |
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.shiro.util; | |
20 | ||
21 | import org.apache.shiro.mgt.SecurityManager; | |
22 | import org.apache.shiro.subject.Subject; | |
23 | import org.slf4j.Logger; | |
24 | import org.slf4j.LoggerFactory; | |
25 | ||
26 | import java.util.Collections; | |
27 | import java.util.HashMap; | |
28 | import java.util.Map; | |
29 | ||
30 | ||
31 | /** | |
32 | * A ThreadContext provides a means of binding and unbinding objects to the | |
33 | * current thread based on key/value pairs. | |
34 | * <p/> | |
35 | * <p>An internal {@link java.util.HashMap} is used to maintain the key/value pairs | |
36 | * for each thread.</p> | |
37 | * <p/> | |
38 | * <p>If the desired behavior is to ensure that bound data is not shared across | |
39 | * threads in a pooled or reusable threaded environment, the application (or more likely a framework) must | |
40 | * bind and remove any necessary values at the beginning and end of stack | |
41 | * execution, respectively (i.e. individually explicitly or all via the <tt>clear</tt> method).</p> | |
42 | * | |
43 | * @see #remove() | |
44 | * @since 0.1 | |
45 | */ | |
46 | public abstract class ThreadContext { | |
47 | ||
48 | /** | |
49 | * Private internal log instance. | |
50 | */ | |
51 | 1 | private static final Logger log = LoggerFactory.getLogger(ThreadContext.class); |
52 | ||
53 | 1 | public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY"; |
54 | 1 | public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY"; |
55 | ||
56 | 1 | private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>(); |
57 | ||
58 | /** | |
59 | * Default no-argument constructor. | |
60 | */ | |
61 | 0 | protected ThreadContext() { |
62 | 0 | } |
63 | ||
64 | /** | |
65 | * Returns the ThreadLocal Map. This Map is used internally to bind objects | |
66 | * to the current thread by storing each object under a unique key. | |
67 | * | |
68 | * @return the map of bound resources | |
69 | */ | |
70 | public static Map<Object, Object> getResources() { | |
71 | 5 | if (resources.get() == null){ |
72 | 5 | return Collections.emptyMap(); |
73 | } else { | |
74 | 0 | return new HashMap<Object, Object>(resources.get()); |
75 | } | |
76 | } | |
77 | ||
78 | /** | |
79 | * Allows a caller to explicitly set the entire resource map. This operation overwrites everything that existed | |
80 | * previously in the ThreadContext - if you need to retain what was on the thread prior to calling this method, | |
81 | * call the {@link #getResources()} method, which will give you the existing state. | |
82 | * | |
83 | * @param newResources the resources to replace the existing {@link #getResources() resources}. | |
84 | * @since 1.0 | |
85 | */ | |
86 | public static void setResources(Map<Object, Object> newResources) { | |
87 | 0 | if (CollectionUtils.isEmpty(newResources)) { |
88 | 0 | return; |
89 | } | |
90 | 0 | ensureResourcesInitialized(); |
91 | 0 | resources.get().clear(); |
92 | 0 | resources.get().putAll(newResources); |
93 | 0 | } |
94 | ||
95 | /** | |
96 | * Returns the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there | |
97 | * is no value for that {@code key}. | |
98 | * | |
99 | * @param key the map key to use to lookup the value | |
100 | * @return the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there | |
101 | * is no value for that {@code key}. | |
102 | * @since 1.0 | |
103 | */ | |
104 | private static Object getValue(Object key) { | |
105 | 87 | Map<Object, Object> perThreadResources = resources.get(); |
106 | 87 | return perThreadResources != null ? perThreadResources.get(key) : null; |
107 | } | |
108 | ||
109 | private static void ensureResourcesInitialized(){ | |
110 | 28 | if (resources.get() == null){ |
111 | 17 | resources.set(new HashMap<Object, Object>()); |
112 | } | |
113 | 28 | } |
114 | ||
115 | /** | |
116 | * Returns the object for the specified <code>key</code> that is bound to | |
117 | * the current thread. | |
118 | * | |
119 | * @param key the key that identifies the value to return | |
120 | * @return the object keyed by <code>key</code> or <code>null</code> if | |
121 | * no value exists for the specified <code>key</code> | |
122 | */ | |
123 | public static Object get(Object key) { | |
124 | 87 | if (log.isTraceEnabled()) { |
125 | 0 | String msg = "get() - in thread [" + Thread.currentThread().getName() + "]"; |
126 | 0 | log.trace(msg); |
127 | } | |
128 | ||
129 | 87 | Object value = getValue(key); |
130 | 87 | if ((value != null) && log.isTraceEnabled()) { |
131 | 0 | String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" + |
132 | key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]"; | |
133 | 0 | log.trace(msg); |
134 | } | |
135 | 87 | return value; |
136 | } | |
137 | ||
138 | /** | |
139 | * Binds <tt>value</tt> for the given <code>key</code> to the current thread. | |
140 | * <p/> | |
141 | * <p>A <tt>null</tt> <tt>value</tt> has the same effect as if <tt>remove</tt> was called for the given | |
142 | * <tt>key</tt>, i.e.: | |
143 | * <p/> | |
144 | * <pre> | |
145 | * if ( value == null ) { | |
146 | * remove( key ); | |
147 | * }</pre> | |
148 | * | |
149 | * @param key The key with which to identify the <code>value</code>. | |
150 | * @param value The value to bind to the thread. | |
151 | * @throws IllegalArgumentException if the <code>key</code> argument is <tt>null</tt>. | |
152 | */ | |
153 | public static void put(Object key, Object value) { | |
154 | 28 | if (key == null) { |
155 | 0 | throw new IllegalArgumentException("key cannot be null"); |
156 | } | |
157 | ||
158 | 28 | if (value == null) { |
159 | 0 | remove(key); |
160 | 0 | return; |
161 | } | |
162 | ||
163 | 28 | ensureResourcesInitialized(); |
164 | 28 | resources.get().put(key, value); |
165 | ||
166 | 28 | if (log.isTraceEnabled()) { |
167 | 0 | String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" + |
168 | key + "] to thread " + "[" + Thread.currentThread().getName() + "]"; | |
169 | 0 | log.trace(msg); |
170 | } | |
171 | 28 | } |
172 | ||
173 | /** | |
174 | * Unbinds the value for the given <code>key</code> from the current | |
175 | * thread. | |
176 | * | |
177 | * @param key The key identifying the value bound to the current thread. | |
178 | * @return the object unbound or <tt>null</tt> if there was nothing bound | |
179 | * under the specified <tt>key</tt> name. | |
180 | */ | |
181 | public static Object remove(Object key) { | |
182 | 0 | Map<Object, Object> perThreadResources = resources.get(); |
183 | 0 | Object value = perThreadResources != null ? perThreadResources.remove(key) : null; |
184 | ||
185 | 0 | if ((value != null) && log.isTraceEnabled()) { |
186 | 0 | String msg = "Removed value of type [" + value.getClass().getName() + "] for key [" + |
187 | key + "]" + "from thread [" + Thread.currentThread().getName() + "]"; | |
188 | 0 | log.trace(msg); |
189 | } | |
190 | ||
191 | 0 | return value; |
192 | } | |
193 | ||
194 | /** | |
195 | * {@link ThreadLocal#remove Remove}s the underlying {@link ThreadLocal ThreadLocal} from the thread. | |
196 | * <p/> | |
197 | * This method is meant to be the final 'clean up' operation that is called at the end of thread execution to | |
198 | * prevent thread corruption in pooled thread environments. | |
199 | * | |
200 | * @since 1.0 | |
201 | */ | |
202 | public static void remove() { | |
203 | 68 | resources.remove(); |
204 | 68 | } |
205 | ||
206 | /** | |
207 | * Convenience method that simplifies retrieval of the application's SecurityManager instance from the current | |
208 | * thread. If there is no SecurityManager bound to the thread (probably because framework code did not bind it | |
209 | * to the thread), this method returns <tt>null</tt>. | |
210 | * <p/> | |
211 | * It is merely a convenient wrapper for the following: | |
212 | * <p/> | |
213 | * <code>return (SecurityManager)get( SECURITY_MANAGER_KEY );</code> | |
214 | * <p/> | |
215 | * This method only returns the bound value if it exists - it does not remove it | |
216 | * from the thread. To remove it, one must call {@link #unbindSecurityManager() unbindSecurityManager()} instead. | |
217 | * | |
218 | * @return the Subject object bound to the thread, or <tt>null</tt> if there isn't one bound. | |
219 | * @since 0.9 | |
220 | */ | |
221 | public static SecurityManager getSecurityManager() { | |
222 | 60 | return (SecurityManager) get(SECURITY_MANAGER_KEY); |
223 | } | |
224 | ||
225 | ||
226 | /** | |
227 | * Convenience method that simplifies binding the application's SecurityManager instance to the ThreadContext. | |
228 | * <p/> | |
229 | * <p>The method's existence is to help reduce casting in code and to simplify remembering of | |
230 | * ThreadContext key names. The implementation is simple in that, if the SecurityManager is not <tt>null</tt>, | |
231 | * it binds it to the thread, i.e.: | |
232 | * <p/> | |
233 | * <pre> | |
234 | * if (securityManager != null) { | |
235 | * put( SECURITY_MANAGER_KEY, securityManager); | |
236 | * }</pre> | |
237 | * | |
238 | * @param securityManager the application's SecurityManager instance to bind to the thread. If the argument is | |
239 | * null, nothing will be done. | |
240 | * @since 0.9 | |
241 | */ | |
242 | public static void bind(SecurityManager securityManager) { | |
243 | 11 | if (securityManager != null) { |
244 | 11 | put(SECURITY_MANAGER_KEY, securityManager); |
245 | } | |
246 | 11 | } |
247 | ||
248 | /** | |
249 | * Convenience method that simplifies removal of the application's SecurityManager instance from the thread. | |
250 | * <p/> | |
251 | * The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is | |
252 | * merely a conveient wrapper for the following: | |
253 | * <p/> | |
254 | * <code>return (SecurityManager)remove( SECURITY_MANAGER_KEY );</code> | |
255 | * <p/> | |
256 | * If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later | |
257 | * during thread execution), use the {@link #getSecurityManager() getSecurityManager()} method instead. | |
258 | * | |
259 | * @return the application's SecurityManager instance previously bound to the thread, or <tt>null</tt> if there | |
260 | * was none bound. | |
261 | * @since 0.9 | |
262 | */ | |
263 | public static SecurityManager unbindSecurityManager() { | |
264 | 0 | return (SecurityManager) remove(SECURITY_MANAGER_KEY); |
265 | } | |
266 | ||
267 | /** | |
268 | * Convenience method that simplifies retrieval of a thread-bound Subject. If there is no | |
269 | * Subject bound to the thread, this method returns <tt>null</tt>. It is merely a convenient wrapper | |
270 | * for the following: | |
271 | * <p/> | |
272 | * <code>return (Subject)get( SUBJECT_KEY );</code> | |
273 | * <p/> | |
274 | * This method only returns the bound value if it exists - it does not remove it | |
275 | * from the thread. To remove it, one must call {@link #unbindSubject() unbindSubject()} instead. | |
276 | * | |
277 | * @return the Subject object bound to the thread, or <tt>null</tt> if there isn't one bound. | |
278 | * @since 0.2 | |
279 | */ | |
280 | public static Subject getSubject() { | |
281 | 27 | return (Subject) get(SUBJECT_KEY); |
282 | } | |
283 | ||
284 | ||
285 | /** | |
286 | * Convenience method that simplifies binding a Subject to the ThreadContext. | |
287 | * <p/> | |
288 | * <p>The method's existence is to help reduce casting in your own code and to simplify remembering of | |
289 | * ThreadContext key names. The implementation is simple in that, if the Subject is not <tt>null</tt>, | |
290 | * it binds it to the thread, i.e.: | |
291 | * <p/> | |
292 | * <pre> | |
293 | * if (subject != null) { | |
294 | * put( SUBJECT_KEY, subject ); | |
295 | * }</pre> | |
296 | * | |
297 | * @param subject the Subject object to bind to the thread. If the argument is null, nothing will be done. | |
298 | * @since 0.2 | |
299 | */ | |
300 | public static void bind(Subject subject) { | |
301 | 17 | if (subject != null) { |
302 | 17 | put(SUBJECT_KEY, subject); |
303 | } | |
304 | 17 | } |
305 | ||
306 | /** | |
307 | * Convenience method that simplifies removal of a thread-local Subject from the thread. | |
308 | * <p/> | |
309 | * The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is | |
310 | * merely a conveient wrapper for the following: | |
311 | * <p/> | |
312 | * <code>return (Subject)remove( SUBJECT_KEY );</code> | |
313 | * <p/> | |
314 | * If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later during | |
315 | * thread execution), you should use the {@link #getSubject() getSubject()} method for that purpose. | |
316 | * | |
317 | * @return the Subject object previously bound to the thread, or <tt>null</tt> if there was none bound. | |
318 | * @since 0.2 | |
319 | */ | |
320 | public static Subject unbindSubject() { | |
321 | 0 | return (Subject) remove(SUBJECT_KEY); |
322 | } | |
323 | ||
324 | 11 | private static final class InheritableThreadLocalMap<T extends Map<Object, Object>> extends InheritableThreadLocal<Map<Object, Object>> { |
325 | ||
326 | /** | |
327 | * This implementation was added to address a | |
328 | * <a href="http://jsecurity.markmail.org/search/?q=#query:+page:1+mid:xqi2yxurwmrpqrvj+state:results"> | |
329 | * user-reported issue</a>. | |
330 | * @param parentValue the parent value, a HashMap as defined in the {@link #initialValue()} method. | |
331 | * @return the HashMap to be used by any parent-spawned child threads (a clone of the parent HashMap). | |
332 | */ | |
333 | @SuppressWarnings({"unchecked"}) | |
334 | protected Map<Object, Object> childValue(Map<Object, Object> parentValue) { | |
335 | 9 | if (parentValue != null) { |
336 | 6 | return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone(); |
337 | } else { | |
338 | 3 | return null; |
339 | } | |
340 | } | |
341 | } | |
342 | } | |
343 |