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