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