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}