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