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.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      private static final Logger log = LoggerFactory.getLogger(ThreadContext.class);
51  
52      public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY";
53      public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY";
54  
55      private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
56  
57      /**
58       * Default no-argument constructor.
59       */
60      protected ThreadContext() {
61      }
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          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          if (CollectionUtils.isEmpty(newResources)) {
83              return;
84          }
85          resources.get().clear();
86          resources.get().putAll(newResources);
87      }
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          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         if (log.isTraceEnabled()) {
112             String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
113             log.trace(msg);
114         }
115 
116         Object value = getValue(key);
117         if ((value != null) && log.isTraceEnabled()) {
118             String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
119                     key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
120             log.trace(msg);
121         }
122         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         if (key == null) {
142             throw new IllegalArgumentException("key cannot be null");
143         }
144 
145         if (value == null) {
146             remove(key);
147             return;
148         }
149 
150         resources.get().put(key, value);
151 
152         if (log.isTraceEnabled()) {
153             String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" +
154                     key + "] to thread " + "[" + Thread.currentThread().getName() + "]";
155             log.trace(msg);
156         }
157     }
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         Object value = resources.get().remove(key);
169 
170         if ((value != null) && log.isTraceEnabled()) {
171             String msg = "Removed value of type [" + value.getClass().getName() + "] for key [" +
172                     key + "]" + "from thread [" + Thread.currentThread().getName() + "]";
173             log.trace(msg);
174         }
175 
176         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         resources.remove();
189     }
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         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         if (securityManager != null) {
229             put(SECURITY_MANAGER_KEY, securityManager);
230         }
231     }
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         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         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         if (subject != null) {
287             put(SUBJECT_KEY, subject);
288         }
289     }
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         return (Subject) remove(SUBJECT_KEY);
307     }
308     
309     private static final class InheritableThreadLocalMap<T extends Map<Object, Object>> extends InheritableThreadLocal<Map<Object, Object>> {
310         protected Map<Object, Object> initialValue() {
311             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             if (parentValue != null) {
324                 return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone();
325             } else {
326                 return null;
327             }
328         }
329     }
330 }
331