001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.util;
020
021 import org.apache.shiro.mgt.SecurityManager;
022 import org.apache.shiro.subject.Subject;
023 import org.slf4j.Logger;
024 import org.slf4j.LoggerFactory;
025
026 import java.util.HashMap;
027 import java.util.Map;
028
029
030 /**
031 * A ThreadContext provides a means of binding and unbinding objects to the
032 * current thread based on key/value pairs.
033 * <p/>
034 * <p>An internal {@link java.util.HashMap} is used to maintain the key/value pairs
035 * for each thread.</p>
036 * <p/>
037 * <p>If the desired behavior is to ensure that bound data is not shared across
038 * threads in a pooled or reusable threaded environment, the application (or more likely a framework) must
039 * bind and remove any necessary values at the beginning and end of stack
040 * execution, respectively (i.e. individually explicitly or all via the <tt>clear</tt> method).</p>
041 *
042 * @author Les Hazlewood
043 * @author Kalle Korhonen
044 * @see #remove()
045 * @since 0.1
046 */
047 public abstract class ThreadContext {
048
049 /**
050 * Private internal log instance.
051 */
052 private static final Logger log = LoggerFactory.getLogger(ThreadContext.class);
053
054 public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY";
055 public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY";
056
057 private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
058
059 /**
060 * Default no-argument constructor.
061 */
062 protected ThreadContext() {
063 }
064
065 /**
066 * Returns the ThreadLocal Map. This Map is used internally to bind objects
067 * to the current thread by storing each object under a unique key.
068 *
069 * @return the map of bound resources
070 */
071 public static Map<Object, Object> getResources() {
072 return resources != null ? new HashMap<Object, Object>(resources.get()) : null;
073 }
074
075 /**
076 * Allows a caller to explicitly set the entire resource map. This operation overwrites everything that existed
077 * previously in the ThreadContext - if you need to retain what was on the thread prior to calling this method,
078 * call the {@link #getResources()} method, which will give you the existing state.
079 *
080 * @param newResources the resources to replace the existing {@link #getResources() resources}.
081 * @since 1.0
082 */
083 public static void setResources(Map<Object, Object> newResources) {
084 if (CollectionUtils.isEmpty(newResources)) {
085 return;
086 }
087 resources.get().clear();
088 resources.get().putAll(newResources);
089 }
090
091 /**
092 * Returns the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there
093 * is no value for that {@code key}.
094 *
095 * @param key the map key to use to lookup the value
096 * @return the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there
097 * is no value for that {@code key}.
098 * @since 1.0
099 */
100 private static Object getValue(Object key) {
101 return resources.get().get(key);
102 }
103
104 /**
105 * Returns the object for the specified <code>key</code> that is bound to
106 * the current thread.
107 *
108 * @param key the key that identifies the value to return
109 * @return the object keyed by <code>key</code> or <code>null</code> if
110 * no value exists for the specified <code>key</code>
111 */
112 public static Object get(Object key) {
113 if (log.isTraceEnabled()) {
114 String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
115 log.trace(msg);
116 }
117
118 Object value = getValue(key);
119 if ((value != null) && log.isTraceEnabled()) {
120 String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
121 key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
122 log.trace(msg);
123 }
124 return value;
125 }
126
127 /**
128 * Binds <tt>value</tt> for the given <code>key</code> to the current thread.
129 * <p/>
130 * <p>A <tt>null</tt> <tt>value</tt> has the same effect as if <tt>remove</tt> was called for the given
131 * <tt>key</tt>, i.e.:
132 * <p/>
133 * <pre>
134 * if ( value == null ) {
135 * remove( key );
136 * }</pre>
137 *
138 * @param key The key with which to identify the <code>value</code>.
139 * @param value The value to bind to the thread.
140 * @throws IllegalArgumentException if the <code>key</code> argument is <tt>null</tt>.
141 */
142 public static void put(Object key, Object value) {
143 if (key == null) {
144 throw new IllegalArgumentException("key cannot be null");
145 }
146
147 if (value == null) {
148 remove(key);
149 return;
150 }
151
152 resources.get().put(key, value);
153
154 if (log.isTraceEnabled()) {
155 String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" +
156 key + "] to thread " + "[" + Thread.currentThread().getName() + "]";
157 log.trace(msg);
158 }
159 }
160
161 /**
162 * Unbinds the value for the given <code>key</code> from the current
163 * thread.
164 *
165 * @param key The key identifying the value bound to the current thread.
166 * @return the object unbound or <tt>null</tt> if there was nothing bound
167 * under the specified <tt>key</tt> name.
168 */
169 public static Object remove(Object key) {
170 Object value = resources.get().remove(key);
171
172 if ((value != null) && log.isTraceEnabled()) {
173 String msg = "Removed value of type [" + value.getClass().getName() + "] for key [" +
174 key + "]" + "from thread [" + Thread.currentThread().getName() + "]";
175 log.trace(msg);
176 }
177
178 return value;
179 }
180
181 /**
182 * {@link ThreadLocal#remove Remove}s the underlying {@link ThreadLocal ThreadLocal} from the thread.
183 * <p/>
184 * This method is meant to be the final 'clean up' operation that is called at the end of thread execution to
185 * prevent thread corruption in pooled thread environments.
186 *
187 * @since 1.0
188 */
189 public static void remove() {
190 resources.remove();
191 }
192
193 /**
194 * Convenience method that simplifies retrieval of the application's SecurityManager instance from the current
195 * thread. If there is no SecurityManager bound to the thread (probably because framework code did not bind it
196 * to the thread), this method returns <tt>null</tt>.
197 * <p/>
198 * It is merely a convenient wrapper for the following:
199 * <p/>
200 * <code>return (SecurityManager)get( SECURITY_MANAGER_KEY );</code>
201 * <p/>
202 * This method only returns the bound value if it exists - it does not remove it
203 * from the thread. To remove it, one must call {@link #unbindSecurityManager() unbindSecurityManager()} instead.
204 *
205 * @return the Subject object bound to the thread, or <tt>null</tt> if there isn't one bound.
206 * @since 0.9
207 */
208 public static SecurityManager getSecurityManager() {
209 return (SecurityManager) get(SECURITY_MANAGER_KEY);
210 }
211
212
213 /**
214 * Convenience method that simplifies binding the application's SecurityManager instance to the ThreadContext.
215 * <p/>
216 * <p>The method's existence is to help reduce casting in code and to simplify remembering of
217 * ThreadContext key names. The implementation is simple in that, if the SecurityManager is not <tt>null</tt>,
218 * it binds it to the thread, i.e.:
219 * <p/>
220 * <pre>
221 * if (securityManager != null) {
222 * put( SECURITY_MANAGER_KEY, securityManager);
223 * }</pre>
224 *
225 * @param securityManager the application's SecurityManager instance to bind to the thread. If the argument is
226 * null, nothing will be done.
227 * @since 0.9
228 */
229 public static void bind(SecurityManager securityManager) {
230 if (securityManager != null) {
231 put(SECURITY_MANAGER_KEY, securityManager);
232 }
233 }
234
235 /**
236 * Convenience method that simplifies removal of the application's SecurityManager instance from the thread.
237 * <p/>
238 * The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is
239 * merely a conveient wrapper for the following:
240 * <p/>
241 * <code>return (SecurityManager)remove( SECURITY_MANAGER_KEY );</code>
242 * <p/>
243 * If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later
244 * during thread execution), use the {@link #getSecurityManager() getSecurityManager()} method instead.
245 *
246 * @return the application's SecurityManager instance previously bound to the thread, or <tt>null</tt> if there
247 * was none bound.
248 * @since 0.9
249 */
250 public static SecurityManager unbindSecurityManager() {
251 return (SecurityManager) remove(SECURITY_MANAGER_KEY);
252 }
253
254 /**
255 * Convenience method that simplifies retrieval of a thread-bound Subject. If there is no
256 * Subject bound to the thread, this method returns <tt>null</tt>. It is merely a convenient wrapper
257 * for the following:
258 * <p/>
259 * <code>return (Subject)get( SUBJECT_KEY );</code>
260 * <p/>
261 * This method only returns the bound value if it exists - it does not remove it
262 * from the thread. To remove it, one must call {@link #unbindSubject() unbindSubject()} instead.
263 *
264 * @return the Subject object bound to the thread, or <tt>null</tt> if there isn't one bound.
265 * @since 0.2
266 */
267 public static Subject getSubject() {
268 return (Subject) get(SUBJECT_KEY);
269 }
270
271
272 /**
273 * Convenience method that simplifies binding a Subject to the ThreadContext.
274 * <p/>
275 * <p>The method's existence is to help reduce casting in your own code and to simplify remembering of
276 * ThreadContext key names. The implementation is simple in that, if the Subject is not <tt>null</tt>,
277 * it binds it to the thread, i.e.:
278 * <p/>
279 * <pre>
280 * if (subject != null) {
281 * put( SUBJECT_KEY, subject );
282 * }</pre>
283 *
284 * @param subject the Subject object to bind to the thread. If the argument is null, nothing will be done.
285 * @since 0.2
286 */
287 public static void bind(Subject subject) {
288 if (subject != null) {
289 put(SUBJECT_KEY, subject);
290 }
291 }
292
293 /**
294 * Convenience method that simplifies removal of a thread-local Subject from the thread.
295 * <p/>
296 * The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is
297 * merely a conveient wrapper for the following:
298 * <p/>
299 * <code>return (Subject)remove( SUBJECT_KEY );</code>
300 * <p/>
301 * If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later during
302 * thread execution), you should use the {@link #getSubject() getSubject()} method for that purpose.
303 *
304 * @return the Subject object previously bound to the thread, or <tt>null</tt> if there was none bound.
305 * @since 0.2
306 */
307 public static Subject unbindSubject() {
308 return (Subject) remove(SUBJECT_KEY);
309 }
310
311 private static final class InheritableThreadLocalMap<T extends Map<Object, Object>> extends InheritableThreadLocal<Map<Object, Object>> {
312 protected Map<Object, Object> initialValue() {
313 return new HashMap<Object, Object>();
314 }
315
316 /**
317 * This implementation was added to address a
318 * <a href="http://jsecurity.markmail.org/search/?q=#query:+page:1+mid:xqi2yxurwmrpqrvj+state:results">
319 * user-reported issue</a>.
320 * @param parentValue the parent value, a HashMap as defined in the {@link #initialValue()} method.
321 * @return the HashMap to be used by any parent-spawned child threads (a clone of the parent HashMap).
322 */
323 @SuppressWarnings({"unchecked"})
324 protected Map<Object, Object> childValue(Map<Object, Object> parentValue) {
325 if (parentValue != null) {
326 return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone();
327 } else {
328 return null;
329 }
330 }
331 }
332 }
333