001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.shiro.util;
020
021import org.apache.shiro.mgt.SecurityManager;
022import org.apache.shiro.subject.Subject;
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026import java.util.Collections;
027import java.util.HashMap;
028import java.util.Map;
029
030
031/**
032 * A ThreadContext provides a means of binding and unbinding objects to the
033 * current thread based on key/value pairs.
034 * <p/>
035 * <p>An internal {@link java.util.HashMap} is used to maintain the key/value pairs
036 * for each thread.</p>
037 * <p/>
038 * <p>If the desired behavior is to ensure that bound data is not shared across
039 * threads in a pooled or reusable threaded environment, the application (or more likely a framework) must
040 * bind and remove any necessary values at the beginning and end of stack
041 * execution, respectively (i.e. individually explicitly or all via the <tt>clear</tt> method).</p>
042 *
043 * @see #remove()
044 * @since 0.1
045 */
046public abstract class ThreadContext {
047
048    /**
049     * security manager key.
050     */
051    public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY";
052
053    /**
054     * subject key.
055     */
056    public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY";
057
058    /**
059     * Private internal log instance.
060     */
061    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadContext.class);
062
063    private static final ThreadLocal<Map<Object, Object>> RESOURCES = new InheritableThreadLocalMap<Map<Object, Object>>();
064
065    /**
066     * Default no-argument constructor.
067     */
068    protected ThreadContext() {
069    }
070
071    /**
072     * Returns the ThreadLocal Map. This Map is used internally to bind objects
073     * to the current thread by storing each object under a unique key.
074     *
075     * @return the map of bound resources
076     */
077    public static Map<Object, Object> getResources() {
078        if (RESOURCES.get() == null) {
079            return Collections.emptyMap();
080        } else {
081            return new HashMap<Object, Object>(RESOURCES.get());
082        }
083    }
084
085    /**
086     * Allows a caller to explicitly set the entire resource map.  This operation overwrites everything that existed
087     * previously in the ThreadContext - if you need to retain what was on the thread prior to calling this method,
088     * call the {@link #getResources()} method, which will give you the existing state.
089     *
090     * @param newResources the resources to replace the existing {@link #getResources() resources}.
091     * @since 1.0
092     */
093    public static void setResources(Map<Object, Object> newResources) {
094        if (CollectionUtils.isEmpty(newResources)) {
095            return;
096        }
097        ensureResourcesInitialized();
098        RESOURCES.get().clear();
099        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