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     */
019    package org.apache.shiro.util;
020    
021    import org.apache.shiro.mgt.SecurityManager;
022    import org.apache.shiro.subject.Subject;
023    import org.slf4j.Logger;
024    import org.slf4j.LoggerFactory;
025    
026    import java.util.Collections;
027    import java.util.HashMap;
028    import 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     */
046    public 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 conveient 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 conveient 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