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 private static final Logger log = LoggerFactory.getLogger(ThreadContext.class); 52 53 public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY"; 54 public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY"; 55 56 private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>(); 57 58 /** 59 * Default no-argument constructor. 60 */ 61 protected ThreadContext() { 62 } 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 if (resources.get() == null){ 72 return Collections.emptyMap(); 73 } else { 74 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 if (CollectionUtils.isEmpty(newResources)) { 88 return; 89 } 90 ensureResourcesInitialized(); 91 resources.get().clear(); 92 resources.get().putAll(newResources); 93 } 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 Map<Object, Object> perThreadResources = resources.get(); 106 return perThreadResources != null ? perThreadResources.get(key) : null; 107 } 108 109 private static void ensureResourcesInitialized(){ 110 if (resources.get() == null){ 111 resources.set(new HashMap<Object, Object>()); 112 } 113 } 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 if (log.isTraceEnabled()) { 125 String msg = "get() - in thread [" + Thread.currentThread().getName() + "]"; 126 log.trace(msg); 127 } 128 129 Object value = getValue(key); 130 if ((value != null) && log.isTraceEnabled()) { 131 String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" + 132 key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]"; 133 log.trace(msg); 134 } 135 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 if (key == null) { 155 throw new IllegalArgumentException("key cannot be null"); 156 } 157 158 if (value == null) { 159 remove(key); 160 return; 161 } 162 163 ensureResourcesInitialized(); 164 resources.get().put(key, value); 165 166 if (log.isTraceEnabled()) { 167 String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" + 168 key + "] to thread " + "[" + Thread.currentThread().getName() + "]"; 169 log.trace(msg); 170 } 171 } 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 Map<Object, Object> perThreadResources = resources.get(); 183 Object value = perThreadResources != null ? perThreadResources.remove(key) : null; 184 185 if ((value != null) && log.isTraceEnabled()) { 186 String msg = "Removed value of type [" + value.getClass().getName() + "] for key [" + 187 key + "]" + "from thread [" + Thread.currentThread().getName() + "]"; 188 log.trace(msg); 189 } 190 191 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 resources.remove(); 204 } 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 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 if (securityManager != null) { 244 put(SECURITY_MANAGER_KEY, securityManager); 245 } 246 } 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 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 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 if (subject != null) { 302 put(SUBJECT_KEY, subject); 303 } 304 } 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 return (Subject) remove(SUBJECT_KEY); 322 } 323 324 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 if (parentValue != null) { 336 return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone(); 337 } else { 338 return null; 339 } 340 } 341 } 342 } 343