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