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     * Private internal log instance.
050     */
051    private static final Logger log = LoggerFactory.getLogger(ThreadContext.class);
052
053    public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY";
054    public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY";
055
056    private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
057
058    /**
059     * Default no-argument constructor.
060     */
061    protected ThreadContext() {
062    }
063
064    /**
065     * Returns the ThreadLocal Map. This Map is used internally to bind objects
066     * to the current thread by storing each object under a unique key.
067     *
068     * @return the map of bound resources
069     */
070    public static Map<Object, Object> getResources() {
071        if (resources.get() == null){
072            return Collections.emptyMap();
073        } else {
074            return new HashMap<Object, Object>(resources.get());
075        }
076    }
077
078    /**
079     * Allows a caller to explicitly set the entire resource map.  This operation overwrites everything that existed
080     * previously in the ThreadContext - if you need to retain what was on the thread prior to calling this method,
081     * call the {@link #getResources()} method, which will give you the existing state.
082     *
083     * @param newResources the resources to replace the existing {@link #getResources() resources}.
084     * @since 1.0
085     */
086    public static void setResources(Map<Object, Object> newResources) {
087        if (CollectionUtils.isEmpty(newResources)) {
088            return;
089        }
090        ensureResourcesInitialized();
091        resources.get().clear();
092        resources.get().putAll(newResources);
093    }
094
095    /**
096     * Returns the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there
097     * is no value for that {@code key}.
098     *
099     * @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