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       * 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