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.HashMap;
027    import java.util.Map;
028    
029    
030    /**
031     * A ThreadContext provides a means of binding and unbinding objects to the
032     * current thread based on key/value pairs.
033     * <p/>
034     * <p>An internal {@link java.util.HashMap} is used to maintain the key/value pairs
035     * for each thread.</p>
036     * <p/>
037     * <p>If the desired behavior is to ensure that bound data is not shared across
038     * threads in a pooled or reusable threaded environment, the application (or more likely a framework) must
039     * bind and remove any necessary values at the beginning and end of stack
040     * execution, respectively (i.e. individually explicitly or all via the <tt>clear</tt> method).</p>
041     *
042     * @author Les Hazlewood
043     * @author Kalle Korhonen
044     * @see #remove()
045     * @since 0.1
046     */
047    public abstract class ThreadContext {
048    
049        /**
050         * Private internal log instance.
051         */
052        private static final Logger log = LoggerFactory.getLogger(ThreadContext.class);
053    
054        public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY";
055        public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY";
056    
057        private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
058    
059        /**
060         * Default no-argument constructor.
061         */
062        protected ThreadContext() {
063        }
064    
065        /**
066         * Returns the ThreadLocal Map. This Map is used internally to bind objects
067         * to the current thread by storing each object under a unique key.
068         *
069         * @return the map of bound resources
070         */
071        public static Map<Object, Object> getResources() {
072            return resources != null ? new HashMap<Object, Object>(resources.get()) : null;
073        }
074    
075        /**
076         * Allows a caller to explicitly set the entire resource map.  This operation overwrites everything that existed
077         * previously in the ThreadContext - if you need to retain what was on the thread prior to calling this method,
078         * call the {@link #getResources()} method, which will give you the existing state.
079         *
080         * @param newResources the resources to replace the existing {@link #getResources() resources}.
081         * @since 1.0
082         */
083        public static void setResources(Map<Object, Object> newResources) {
084            if (CollectionUtils.isEmpty(newResources)) {
085                return;
086            }
087            resources.get().clear();
088            resources.get().putAll(newResources);
089        }
090    
091        /**
092         * Returns the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there
093         * is no value for that {@code key}.
094         *
095         * @param key the map key to use to lookup the value
096         * @return 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         * @since 1.0
099         */
100        private static Object getValue(Object key) {
101            return resources.get().get(key);
102        }
103    
104        /**
105         * Returns the object for the specified <code>key</code> that is bound to
106         * the current thread.
107         *
108         * @param key the key that identifies the value to return
109         * @return the object keyed by <code>key</code> or <code>null</code> if
110         *         no value exists for the specified <code>key</code>
111         */
112        public static Object get(Object key) {
113            if (log.isTraceEnabled()) {
114                String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
115                log.trace(msg);
116            }
117    
118            Object value = getValue(key);
119            if ((value != null) && log.isTraceEnabled()) {
120                String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
121                        key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
122                log.trace(msg);
123            }
124            return value;
125        }
126    
127        /**
128         * Binds <tt>value</tt> for the given <code>key</code> to the current thread.
129         * <p/>
130         * <p>A <tt>null</tt> <tt>value</tt> has the same effect as if <tt>remove</tt> was called for the given
131         * <tt>key</tt>, i.e.:
132         * <p/>
133         * <pre>
134         * if ( value == null ) {
135         *     remove( key );
136         * }</pre>
137         *
138         * @param key   The key with which to identify the <code>value</code>.
139         * @param value The value to bind to the thread.
140         * @throws IllegalArgumentException if the <code>key</code> argument is <tt>null</tt>.
141         */
142        public static void put(Object key, Object value) {
143            if (key == null) {
144                throw new IllegalArgumentException("key cannot be null");
145            }
146    
147            if (value == null) {
148                remove(key);
149                return;
150            }
151    
152            resources.get().put(key, value);
153    
154            if (log.isTraceEnabled()) {
155                String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" +
156                        key + "] to thread " + "[" + Thread.currentThread().getName() + "]";
157                log.trace(msg);
158            }
159        }
160    
161        /**
162         * Unbinds the value for the given <code>key</code> from the current
163         * thread.
164         *
165         * @param key The key identifying the value bound to the current thread.
166         * @return the object unbound or <tt>null</tt> if there was nothing bound
167         *         under the specified <tt>key</tt> name.
168         */
169        public static Object remove(Object key) {
170            Object value = resources.get().remove(key);
171    
172            if ((value != null) && log.isTraceEnabled()) {
173                String msg = "Removed value of type [" + value.getClass().getName() + "] for key [" +
174                        key + "]" + "from thread [" + Thread.currentThread().getName() + "]";
175                log.trace(msg);
176            }
177    
178            return value;
179        }
180    
181        /**
182         * {@link ThreadLocal#remove Remove}s the underlying {@link ThreadLocal ThreadLocal} from the thread.
183         * <p/>
184         * This method is meant to be the final 'clean up' operation that is called at the end of thread execution to
185         * prevent thread corruption in pooled thread environments.
186         *
187         * @since 1.0
188         */
189        public static void remove() {
190            resources.remove();
191        }
192    
193        /**
194         * Convenience method that simplifies retrieval of the application's SecurityManager instance from the current
195         * thread. If there is no SecurityManager bound to the thread (probably because framework code did not bind it
196         * to the thread), this method returns <tt>null</tt>.
197         * <p/>
198         * It is merely a convenient wrapper for the following:
199         * <p/>
200         * <code>return (SecurityManager)get( SECURITY_MANAGER_KEY );</code>
201         * <p/>
202         * This method only returns the bound value if it exists - it does not remove it
203         * from the thread.  To remove it, one must call {@link #unbindSecurityManager() unbindSecurityManager()} instead.
204         *
205         * @return the Subject object bound to the thread, or <tt>null</tt> if there isn't one bound.
206         * @since 0.9
207         */
208        public static SecurityManager getSecurityManager() {
209            return (SecurityManager) get(SECURITY_MANAGER_KEY);
210        }
211    
212    
213        /**
214         * Convenience method that simplifies binding the application's SecurityManager instance to the ThreadContext.
215         * <p/>
216         * <p>The method's existence is to help reduce casting in code and to simplify remembering of
217         * ThreadContext key names.  The implementation is simple in that, if the SecurityManager is not <tt>null</tt>,
218         * it binds it to the thread, i.e.:
219         * <p/>
220         * <pre>
221         * if (securityManager != null) {
222         *     put( SECURITY_MANAGER_KEY, securityManager);
223         * }</pre>
224         *
225         * @param securityManager the application's SecurityManager instance to bind to the thread.  If the argument is
226         *                        null, nothing will be done.
227         * @since 0.9
228         */
229        public static void bind(SecurityManager securityManager) {
230            if (securityManager != null) {
231                put(SECURITY_MANAGER_KEY, securityManager);
232            }
233        }
234    
235        /**
236         * Convenience method that simplifies removal of the application's SecurityManager instance from the thread.
237         * <p/>
238         * The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is
239         * merely a conveient wrapper for the following:
240         * <p/>
241         * <code>return (SecurityManager)remove( SECURITY_MANAGER_KEY );</code>
242         * <p/>
243         * If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later
244         * during thread execution), use the {@link #getSecurityManager() getSecurityManager()} method instead.
245         *
246         * @return the application's SecurityManager instance previously bound to the thread, or <tt>null</tt> if there
247         *         was none bound.
248         * @since 0.9
249         */
250        public static SecurityManager unbindSecurityManager() {
251            return (SecurityManager) remove(SECURITY_MANAGER_KEY);
252        }
253    
254        /**
255         * Convenience method that simplifies retrieval of a thread-bound Subject.  If there is no
256         * Subject bound to the thread, this method returns <tt>null</tt>.  It is merely a convenient wrapper
257         * for the following:
258         * <p/>
259         * <code>return (Subject)get( SUBJECT_KEY );</code>
260         * <p/>
261         * This method only returns the bound value if it exists - it does not remove it
262         * from the thread.  To remove it, one must call {@link #unbindSubject() unbindSubject()} instead.
263         *
264         * @return the Subject object bound to the thread, or <tt>null</tt> if there isn't one bound.
265         * @since 0.2
266         */
267        public static Subject getSubject() {
268            return (Subject) get(SUBJECT_KEY);
269        }
270    
271    
272        /**
273         * Convenience method that simplifies binding a Subject to the ThreadContext.
274         * <p/>
275         * <p>The method's existence is to help reduce casting in your own code and to simplify remembering of
276         * ThreadContext key names.  The implementation is simple in that, if the Subject is not <tt>null</tt>,
277         * it binds it to the thread, i.e.:
278         * <p/>
279         * <pre>
280         * if (subject != null) {
281         *     put( SUBJECT_KEY, subject );
282         * }</pre>
283         *
284         * @param subject the Subject object to bind to the thread.  If the argument is null, nothing will be done.
285         * @since 0.2
286         */
287        public static void bind(Subject subject) {
288            if (subject != null) {
289                put(SUBJECT_KEY, subject);
290            }
291        }
292    
293        /**
294         * Convenience method that simplifies removal of a thread-local Subject from the thread.
295         * <p/>
296         * The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is
297         * merely a conveient wrapper for the following:
298         * <p/>
299         * <code>return (Subject)remove( SUBJECT_KEY );</code>
300         * <p/>
301         * If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later during
302         * thread execution), you should use the {@link #getSubject() getSubject()} method for that purpose.
303         *
304         * @return the Subject object previously bound to the thread, or <tt>null</tt> if there was none bound.
305         * @since 0.2
306         */
307        public static Subject unbindSubject() {
308            return (Subject) remove(SUBJECT_KEY);
309        }
310        
311        private static final class InheritableThreadLocalMap<T extends Map<Object, Object>> extends InheritableThreadLocal<Map<Object, Object>> {
312            protected Map<Object, Object> initialValue() {
313                return new HashMap<Object, Object>();
314            }
315    
316            /**
317             * This implementation was added to address a
318             * <a href="http://jsecurity.markmail.org/search/?q=#query:+page:1+mid:xqi2yxurwmrpqrvj+state:results">
319             * user-reported issue</a>.
320             * @param parentValue the parent value, a HashMap as defined in the {@link #initialValue()} method.
321             * @return the HashMap to be used by any parent-spawned child threads (a clone of the parent HashMap).
322             */
323            @SuppressWarnings({"unchecked"})
324            protected Map<Object, Object> childValue(Map<Object, Object> parentValue) {
325                if (parentValue != null) {
326                    return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone();
327                } else {
328                    return null;
329                }
330            }
331        }
332    }
333