View Javadoc
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 convenient 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 convenient 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