001/*
002 * Copyright 2002-2012 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.util.ClassUtils;
024
025/**
026 * Holder class to expose the web request in the form of a thread-bound
027 * {@link RequestAttributes} object. The request will be inherited
028 * by any child threads spawned by the current thread if the
029 * {@code inheritable} flag is set to {@code true}.
030 *
031 * <p>Use {@link RequestContextListener} or
032 * {@link org.springframework.web.filter.RequestContextFilter} to expose
033 * the current web request. Note that
034 * {@link org.springframework.web.servlet.DispatcherServlet} and
035 * {@link org.springframework.web.portlet.DispatcherPortlet} already
036 * expose 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 * @see org.springframework.web.portlet.DispatcherPortlet
045 */
046public abstract class RequestContextHolder  {
047
048        private static final boolean jsfPresent =
049                        ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
050
051        private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
052                        new NamedThreadLocal<RequestAttributes>("Request attributes");
053
054        private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
055                        new NamedInheritableThreadLocal<RequestAttributes>("Request context");
056
057
058        /**
059         * Reset the RequestAttributes for the current thread.
060         */
061        public static void resetRequestAttributes() {
062                requestAttributesHolder.remove();
063                inheritableRequestAttributesHolder.remove();
064        }
065
066        /**
067         * Bind the given RequestAttributes to the current thread,
068         * <i>not</i> exposing it as inheritable for child threads.
069         * @param attributes the RequestAttributes to expose
070         * @see #setRequestAttributes(RequestAttributes, boolean)
071         */
072        public static void setRequestAttributes(RequestAttributes attributes) {
073                setRequestAttributes(attributes, false);
074        }
075
076        /**
077         * Bind the given RequestAttributes to the current thread.
078         * @param attributes the RequestAttributes to expose,
079         * or {@code null} to reset the thread-bound context
080         * @param inheritable whether to expose the RequestAttributes as inheritable
081         * for child threads (using an {@link InheritableThreadLocal})
082         */
083        public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {
084                if (attributes == null) {
085                        resetRequestAttributes();
086                }
087                else {
088                        if (inheritable) {
089                                inheritableRequestAttributesHolder.set(attributes);
090                                requestAttributesHolder.remove();
091                        }
092                        else {
093                                requestAttributesHolder.set(attributes);
094                                inheritableRequestAttributesHolder.remove();
095                        }
096                }
097        }
098
099        /**
100         * Return the RequestAttributes currently bound to the thread.
101         * @return the RequestAttributes currently bound to the thread,
102         * or {@code null} if none bound
103         */
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/DispatcherPortlet: " +
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                public static RequestAttributes getFacesRequestAttributes() {
149                        FacesContext facesContext = FacesContext.getCurrentInstance();
150                        return (facesContext != null ? new FacesRequestAttributes(facesContext) : null);
151                }
152        }
153
154}