001/*
002 * Copyright 2002-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.context.request;
018
019import javax.faces.context.FacesContext;
020
021import org.springframework.core.NamedInheritableThreadLocal;
022import org.springframework.core.NamedThreadLocal;
023import org.springframework.lang.Nullable;
024import org.springframework.util.ClassUtils;
025
026/**
027 * Holder class to expose the web request in the form of a thread-bound
028 * {@link RequestAttributes} object. The request will be inherited
029 * by any child threads spawned by the current thread if the
030 * {@code inheritable} flag is set to {@code true}.
031 *
032 * <p>Use {@link RequestContextListener} or
033 * {@link org.springframework.web.filter.RequestContextFilter} to expose
034 * the current web request. Note that
035 * {@link org.springframework.web.servlet.DispatcherServlet}
036 * already exposes the current request by default.
037 *
038 * @author Juergen Hoeller
039 * @author Rod Johnson
040 * @since 2.0
041 * @see RequestContextListener
042 * @see org.springframework.web.filter.RequestContextFilter
043 * @see org.springframework.web.servlet.DispatcherServlet
044 */
045public abstract class RequestContextHolder  {
046
047        private static final boolean jsfPresent =
048                        ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
049
050        private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
051                        new NamedThreadLocal<>("Request attributes");
052
053        private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
054                        new NamedInheritableThreadLocal<>("Request context");
055
056
057        /**
058         * Reset the RequestAttributes for the current thread.
059         */
060        public static void resetRequestAttributes() {
061                requestAttributesHolder.remove();
062                inheritableRequestAttributesHolder.remove();
063        }
064
065        /**
066         * Bind the given RequestAttributes to the current thread,
067         * <i>not</i> exposing it as inheritable for child threads.
068         * @param attributes the RequestAttributes to expose
069         * @see #setRequestAttributes(RequestAttributes, boolean)
070         */
071        public static void setRequestAttributes(@Nullable RequestAttributes attributes) {
072                setRequestAttributes(attributes, false);
073        }
074
075        /**
076         * Bind the given RequestAttributes to the current thread.
077         * @param attributes the RequestAttributes to expose,
078         * or {@code null} to reset the thread-bound context
079         * @param inheritable whether to expose the RequestAttributes as inheritable
080         * for child threads (using an {@link InheritableThreadLocal})
081         */
082        public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
083                if (attributes == null) {
084                        resetRequestAttributes();
085                }
086                else {
087                        if (inheritable) {
088                                inheritableRequestAttributesHolder.set(attributes);
089                                requestAttributesHolder.remove();
090                        }
091                        else {
092                                requestAttributesHolder.set(attributes);
093                                inheritableRequestAttributesHolder.remove();
094                        }
095                }
096        }
097
098        /**
099         * Return the RequestAttributes currently bound to the thread.
100         * @return the RequestAttributes currently bound to the thread,
101         * or {@code null} if none bound
102         */
103        @Nullable
104        public static RequestAttributes getRequestAttributes() {
105                RequestAttributes attributes = requestAttributesHolder.get();
106                if (attributes == null) {
107                        attributes = inheritableRequestAttributesHolder.get();
108                }
109                return attributes;
110        }
111
112        /**
113         * Return the RequestAttributes currently bound to the thread.
114         * <p>Exposes the previously bound RequestAttributes instance, if any.
115         * Falls back to the current JSF FacesContext, if any.
116         * @return the RequestAttributes currently bound to the thread
117         * @throws IllegalStateException if no RequestAttributes object
118         * is bound to the current thread
119         * @see #setRequestAttributes
120         * @see ServletRequestAttributes
121         * @see FacesRequestAttributes
122         * @see javax.faces.context.FacesContext#getCurrentInstance()
123         */
124        public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
125                RequestAttributes attributes = getRequestAttributes();
126                if (attributes == null) {
127                        if (jsfPresent) {
128                                attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
129                        }
130                        if (attributes == null) {
131                                throw new IllegalStateException("No thread-bound request found: " +
132                                                "Are you referring to request attributes outside of an actual web request, " +
133                                                "or processing a request outside of the originally receiving thread? " +
134                                                "If you are actually operating within a web request and still receive this message, " +
135                                                "your code is probably running outside of DispatcherServlet: " +
136                                                "In this case, use RequestContextListener or RequestContextFilter to expose the current request.");
137                        }
138                }
139                return attributes;
140        }
141
142
143        /**
144         * Inner class to avoid hard-coded JSF dependency.
145         */
146        private static class FacesRequestAttributesFactory {
147
148                @Nullable
149                public static RequestAttributes getFacesRequestAttributes() {
150                        FacesContext facesContext = FacesContext.getCurrentInstance();
151                        return (facesContext != null ? new FacesRequestAttributes(facesContext) : null);
152                }
153        }
154
155}