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