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.subject.support;
020    
021    import org.apache.shiro.mgt.SecurityManager;
022    import org.apache.shiro.subject.Subject;
023    import org.apache.shiro.util.CollectionUtils;
024    import org.apache.shiro.util.ThreadContext;
025    import org.apache.shiro.util.ThreadState;
026    
027    import java.util.Map;
028    
029    /**
030     * Manages thread-state for {@link Subject Subject} access (supporting
031     * {@code SecurityUtils.}{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()} calls)
032     * during a thread's execution.
033     * <p/>
034     * The {@link #bind bind} method will bind a {@link Subject} and a
035     * {@link org.apache.shiro.mgt.SecurityManager SecurityManager} to the {@link ThreadContext} so they can be retrieved
036     * from the {@code ThreadContext} later by any
037     * {@code SecurityUtils.}{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()} calls that might occur during
038     * the thread's execution.
039     *
040     * @since 1.0
041     * @author Les Hazlewood
042     */
043    public class SubjectThreadState implements ThreadState {
044    
045        private Map<Object, Object> originalResources;
046    
047        private final Subject subject;
048        private transient SecurityManager securityManager;
049    
050        /**
051         * Creates a new {@code SubjectThreadState} that will bind and unbind the specified {@code Subject} to the
052         * thread
053         *
054         * @param subject the {@code Subject} instance to bind and unbind from the {@link ThreadContext}.
055         */
056        public SubjectThreadState(Subject subject) {
057            if (subject == null) {
058                throw new IllegalArgumentException("Subject argument cannot be null.");
059            }
060            this.subject = subject;
061    
062            SecurityManager securityManager = null;
063            if ( subject instanceof DelegatingSubject) {
064                securityManager = ((DelegatingSubject)subject).getSecurityManager();
065            }
066            if ( securityManager == null) {
067                securityManager = ThreadContext.getSecurityManager();
068            }
069            this.securityManager = securityManager;
070        }
071    
072        /**
073         * Returns the {@code Subject} instance managed by this {@code ThreadState} implementation.
074         *
075         * @return the {@code Subject} instance managed by this {@code ThreadState} implementation.
076         */
077        protected Subject getSubject() {
078            return this.subject;
079        }
080    
081        /**
082         * Binds a {@link Subject} and {@link org.apache.shiro.mgt.SecurityManager SecurityManager} to the
083         * {@link ThreadContext} so they can be retrieved later by any
084         * {@code SecurityUtils.}{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()} calls that might occur
085         * during the thread's execution.
086         * <p/>
087         * Prior to binding, the {@code ThreadContext}'s existing {@link ThreadContext#getResources() resources} are
088         * retained so they can be restored later via the {@link #restore restore} call.
089         */
090        public void bind() {
091            SecurityManager securityManager = this.securityManager;
092            if ( securityManager == null ) {
093                //try just in case the constructor didn't find one at the time:
094                securityManager = ThreadContext.getSecurityManager();
095            }
096            this.originalResources = ThreadContext.getResources();
097            ThreadContext.remove();
098    
099            ThreadContext.bind(this.subject);
100            if (securityManager != null) {
101                ThreadContext.bind(securityManager);
102            }
103        }
104    
105        /**
106         * {@link ThreadContext#remove Remove}s all thread-state that was bound by this instance.  If any previous
107         * thread-bound resources existed prior to the {@link #bind bind} call, they are restored back to the
108         * {@code ThreadContext} to ensure the thread state is exactly as it was before binding.
109         */
110        public void restore() {
111            ThreadContext.remove();
112            if (!CollectionUtils.isEmpty(this.originalResources)) {
113                ThreadContext.setResources(this.originalResources);
114            }
115        }
116    
117        /**
118         * Completely {@link ThreadContext#remove removes} the {@code ThreadContext} state.  Typically this method should
119         * only be called in special cases - it is more 'correct' to {@link #restore restore} a thread to its previous
120         * state than to clear it entirely.
121         */
122        public void clear() {
123            ThreadContext.remove();
124        }
125    }