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