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.support; 018 019import java.io.Serializable; 020import java.util.Collections; 021import java.util.Enumeration; 022import java.util.HashMap; 023import java.util.Map; 024import javax.faces.context.ExternalContext; 025import javax.faces.context.FacesContext; 026import javax.servlet.ServletConfig; 027import javax.servlet.ServletContext; 028import javax.servlet.ServletRequest; 029import javax.servlet.ServletResponse; 030import javax.servlet.http.HttpSession; 031 032import org.springframework.beans.factory.ObjectFactory; 033import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 034import org.springframework.core.env.MutablePropertySources; 035import org.springframework.core.env.PropertySource.StubPropertySource; 036import org.springframework.util.Assert; 037import org.springframework.util.ClassUtils; 038import org.springframework.web.context.ConfigurableWebApplicationContext; 039import org.springframework.web.context.WebApplicationContext; 040import org.springframework.web.context.request.RequestAttributes; 041import org.springframework.web.context.request.RequestContextHolder; 042import org.springframework.web.context.request.RequestScope; 043import org.springframework.web.context.request.ServletRequestAttributes; 044import org.springframework.web.context.request.ServletWebRequest; 045import org.springframework.web.context.request.SessionScope; 046import org.springframework.web.context.request.WebRequest; 047 048/** 049 * Convenience methods for retrieving the root {@link WebApplicationContext} for 050 * a given {@link ServletContext}. This is useful for programmatically accessing 051 * a Spring application context from within custom web views or MVC actions. 052 * 053 * <p>Note that there are more convenient ways of accessing the root context for 054 * many web frameworks, either part of Spring or available as an external library. 055 * This helper class is just the most generic way to access the root context. 056 * 057 * @author Juergen Hoeller 058 * @see org.springframework.web.context.ContextLoader 059 * @see org.springframework.web.servlet.FrameworkServlet 060 * @see org.springframework.web.servlet.DispatcherServlet 061 * @see org.springframework.web.jsf.FacesContextUtils 062 * @see org.springframework.web.jsf.el.SpringBeanFacesELResolver 063 */ 064public abstract class WebApplicationContextUtils { 065 066 private static final boolean jsfPresent = 067 ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader()); 068 069 070 /** 071 * Find the root {@code WebApplicationContext} for this web app, typically 072 * loaded via {@link org.springframework.web.context.ContextLoaderListener}. 073 * <p>Will rethrow an exception that happened on root context startup, 074 * to differentiate between a failed context startup and no context at all. 075 * @param sc ServletContext to find the web application context for 076 * @return the root WebApplicationContext for this web app 077 * @throws IllegalStateException if the root WebApplicationContext could not be found 078 * @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 079 */ 080 public static WebApplicationContext getRequiredWebApplicationContext(ServletContext sc) throws IllegalStateException { 081 WebApplicationContext wac = getWebApplicationContext(sc); 082 if (wac == null) { 083 throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?"); 084 } 085 return wac; 086 } 087 088 /** 089 * Find the root {@code WebApplicationContext} for this web app, typically 090 * loaded via {@link org.springframework.web.context.ContextLoaderListener}. 091 * <p>Will rethrow an exception that happened on root context startup, 092 * to differentiate between a failed context startup and no context at all. 093 * @param sc ServletContext to find the web application context for 094 * @return the root WebApplicationContext for this web app, or {@code null} if none 095 * @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 096 */ 097 public static WebApplicationContext getWebApplicationContext(ServletContext sc) { 098 return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); 099 } 100 101 /** 102 * Find a custom {@code WebApplicationContext} for this web app. 103 * @param sc ServletContext to find the web application context for 104 * @param attrName the name of the ServletContext attribute to look for 105 * @return the desired WebApplicationContext for this web app, or {@code null} if none 106 */ 107 public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) { 108 Assert.notNull(sc, "ServletContext must not be null"); 109 Object attr = sc.getAttribute(attrName); 110 if (attr == null) { 111 return null; 112 } 113 if (attr instanceof RuntimeException) { 114 throw (RuntimeException) attr; 115 } 116 if (attr instanceof Error) { 117 throw (Error) attr; 118 } 119 if (attr instanceof Exception) { 120 throw new IllegalStateException((Exception) attr); 121 } 122 if (!(attr instanceof WebApplicationContext)) { 123 throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr); 124 } 125 return (WebApplicationContext) attr; 126 } 127 128 /** 129 * Find a unique {@code WebApplicationContext} for this web app: either the 130 * root web app context (preferred) or a unique {@code WebApplicationContext} 131 * among the registered {@code ServletContext} attributes (typically coming 132 * from a single {@code DispatcherServlet} in the current web application). 133 * <p>Note that {@code DispatcherServlet}'s exposure of its context can be 134 * controlled through its {@code publishContext} property, which is {@code true} 135 * by default but can be selectively switched to only publish a single context 136 * despite multiple {@code DispatcherServlet} registrations in the web app. 137 * @param sc ServletContext to find the web application context for 138 * @return the desired WebApplicationContext for this web app, or {@code null} if none 139 * @since 4.2 140 * @see #getWebApplicationContext(ServletContext) 141 * @see ServletContext#getAttributeNames() 142 */ 143 public static WebApplicationContext findWebApplicationContext(ServletContext sc) { 144 WebApplicationContext wac = getWebApplicationContext(sc); 145 if (wac == null) { 146 Enumeration<String> attrNames = sc.getAttributeNames(); 147 while (attrNames.hasMoreElements()) { 148 String attrName = attrNames.nextElement(); 149 Object attrValue = sc.getAttribute(attrName); 150 if (attrValue instanceof WebApplicationContext) { 151 if (wac != null) { 152 throw new IllegalStateException("No unique WebApplicationContext found: more than one " + 153 "DispatcherServlet registered with publishContext=true?"); 154 } 155 wac = (WebApplicationContext) attrValue; 156 } 157 } 158 } 159 return wac; 160 } 161 162 163 /** 164 * Register web-specific scopes ("request", "session", "globalSession") 165 * with the given BeanFactory, as used by the WebApplicationContext. 166 * @param beanFactory the BeanFactory to configure 167 */ 168 public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory) { 169 registerWebApplicationScopes(beanFactory, null); 170 } 171 172 /** 173 * Register web-specific scopes ("request", "session", "globalSession", "application") 174 * with the given BeanFactory, as used by the WebApplicationContext. 175 * @param beanFactory the BeanFactory to configure 176 * @param sc the ServletContext that we're running within 177 */ 178 public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) { 179 beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope()); 180 beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false)); 181 beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true)); 182 if (sc != null) { 183 ServletContextScope appScope = new ServletContextScope(sc); 184 beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope); 185 // Register as ServletContext attribute, for ContextCleanupListener to detect it. 186 sc.setAttribute(ServletContextScope.class.getName(), appScope); 187 } 188 189 beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); 190 beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory()); 191 beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory()); 192 beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); 193 if (jsfPresent) { 194 FacesDependencyRegistrar.registerFacesDependencies(beanFactory); 195 } 196 } 197 198 /** 199 * Register web-specific environment beans ("contextParameters", "contextAttributes") 200 * with the given BeanFactory, as used by the WebApplicationContext. 201 * @param bf the BeanFactory to configure 202 * @param sc the ServletContext that we're running within 203 */ 204 public static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf, ServletContext sc) { 205 registerEnvironmentBeans(bf, sc, null); 206 } 207 208 /** 209 * Register web-specific environment beans ("contextParameters", "contextAttributes") 210 * with the given BeanFactory, as used by the WebApplicationContext. 211 * @param bf the BeanFactory to configure 212 * @param servletContext the ServletContext that we're running within 213 * @param servletConfig the ServletConfig of the containing Portlet 214 */ 215 public static void registerEnvironmentBeans( 216 ConfigurableListableBeanFactory bf, ServletContext servletContext, ServletConfig servletConfig) { 217 218 if (servletContext != null && !bf.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)) { 219 bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext); 220 } 221 222 if (servletConfig != null && !bf.containsBean(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME)) { 223 bf.registerSingleton(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME, servletConfig); 224 } 225 226 if (!bf.containsBean(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME)) { 227 Map<String, String> parameterMap = new HashMap<String, String>(); 228 if (servletContext != null) { 229 Enumeration<?> paramNameEnum = servletContext.getInitParameterNames(); 230 while (paramNameEnum.hasMoreElements()) { 231 String paramName = (String) paramNameEnum.nextElement(); 232 parameterMap.put(paramName, servletContext.getInitParameter(paramName)); 233 } 234 } 235 if (servletConfig != null) { 236 Enumeration<?> paramNameEnum = servletConfig.getInitParameterNames(); 237 while (paramNameEnum.hasMoreElements()) { 238 String paramName = (String) paramNameEnum.nextElement(); 239 parameterMap.put(paramName, servletConfig.getInitParameter(paramName)); 240 } 241 } 242 bf.registerSingleton(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME, 243 Collections.unmodifiableMap(parameterMap)); 244 } 245 246 if (!bf.containsBean(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME)) { 247 Map<String, Object> attributeMap = new HashMap<String, Object>(); 248 if (servletContext != null) { 249 Enumeration<?> attrNameEnum = servletContext.getAttributeNames(); 250 while (attrNameEnum.hasMoreElements()) { 251 String attrName = (String) attrNameEnum.nextElement(); 252 attributeMap.put(attrName, servletContext.getAttribute(attrName)); 253 } 254 } 255 bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME, 256 Collections.unmodifiableMap(attributeMap)); 257 } 258 } 259 260 /** 261 * Convenient variant of {@link #initServletPropertySources(MutablePropertySources, 262 * ServletContext, ServletConfig)} that always provides {@code null} for the 263 * {@link ServletConfig} parameter. 264 * @see #initServletPropertySources(MutablePropertySources, ServletContext, ServletConfig) 265 */ 266 public static void initServletPropertySources(MutablePropertySources propertySources, ServletContext servletContext) { 267 initServletPropertySources(propertySources, servletContext, null); 268 } 269 270 /** 271 * Replace {@code Servlet}-based {@link StubPropertySource stub property sources} with 272 * actual instances populated with the given {@code servletContext} and 273 * {@code servletConfig} objects. 274 * <p>This method is idempotent with respect to the fact it may be called any number 275 * of times but will perform replacement of stub property sources with their 276 * corresponding actual property sources once and only once. 277 * @param propertySources the {@link MutablePropertySources} to initialize (must not 278 * be {@code null}) 279 * @param servletContext the current {@link ServletContext} (ignored if {@code null} 280 * or if the {@link StandardServletEnvironment#SERVLET_CONTEXT_PROPERTY_SOURCE_NAME 281 * servlet context property source} has already been initialized) 282 * @param servletConfig the current {@link ServletConfig} (ignored if {@code null} 283 * or if the {@link StandardServletEnvironment#SERVLET_CONFIG_PROPERTY_SOURCE_NAME 284 * servlet config property source} has already been initialized) 285 * @see org.springframework.core.env.PropertySource.StubPropertySource 286 * @see org.springframework.core.env.ConfigurableEnvironment#getPropertySources() 287 */ 288 public static void initServletPropertySources( 289 MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) { 290 291 Assert.notNull(propertySources, "'propertySources' must not be null"); 292 if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) && 293 propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) { 294 propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, 295 new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext)); 296 } 297 if (servletConfig != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) && 298 propertySources.get(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) { 299 propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, 300 new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig)); 301 } 302 } 303 304 /** 305 * Return the current RequestAttributes instance as ServletRequestAttributes. 306 * @see RequestContextHolder#currentRequestAttributes() 307 */ 308 private static ServletRequestAttributes currentRequestAttributes() { 309 RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes(); 310 if (!(requestAttr instanceof ServletRequestAttributes)) { 311 throw new IllegalStateException("Current request is not a servlet request"); 312 } 313 return (ServletRequestAttributes) requestAttr; 314 } 315 316 317 /** 318 * Factory that exposes the current request object on demand. 319 */ 320 @SuppressWarnings("serial") 321 private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable { 322 323 @Override 324 public ServletRequest getObject() { 325 return currentRequestAttributes().getRequest(); 326 } 327 328 @Override 329 public String toString() { 330 return "Current HttpServletRequest"; 331 } 332 } 333 334 335 /** 336 * Factory that exposes the current response object on demand. 337 */ 338 @SuppressWarnings("serial") 339 private static class ResponseObjectFactory implements ObjectFactory<ServletResponse>, Serializable { 340 341 @Override 342 public ServletResponse getObject() { 343 ServletResponse response = currentRequestAttributes().getResponse(); 344 if (response == null) { 345 throw new IllegalStateException("Current servlet response not available - " + 346 "consider using RequestContextFilter instead of RequestContextListener"); 347 } 348 return response; 349 } 350 351 @Override 352 public String toString() { 353 return "Current HttpServletResponse"; 354 } 355 } 356 357 358 /** 359 * Factory that exposes the current session object on demand. 360 */ 361 @SuppressWarnings("serial") 362 private static class SessionObjectFactory implements ObjectFactory<HttpSession>, Serializable { 363 364 @Override 365 public HttpSession getObject() { 366 return currentRequestAttributes().getRequest().getSession(); 367 } 368 369 @Override 370 public String toString() { 371 return "Current HttpSession"; 372 } 373 } 374 375 376 /** 377 * Factory that exposes the current WebRequest object on demand. 378 */ 379 @SuppressWarnings("serial") 380 private static class WebRequestObjectFactory implements ObjectFactory<WebRequest>, Serializable { 381 382 @Override 383 public WebRequest getObject() { 384 ServletRequestAttributes requestAttr = currentRequestAttributes(); 385 return new ServletWebRequest(requestAttr.getRequest(), requestAttr.getResponse()); 386 } 387 388 @Override 389 public String toString() { 390 return "Current ServletWebRequest"; 391 } 392 } 393 394 395 /** 396 * Inner class to avoid hard-coded JSF dependency. 397 */ 398 private static class FacesDependencyRegistrar { 399 400 public static void registerFacesDependencies(ConfigurableListableBeanFactory beanFactory) { 401 beanFactory.registerResolvableDependency(FacesContext.class, new ObjectFactory<FacesContext>() { 402 @Override 403 public FacesContext getObject() { 404 return FacesContext.getCurrentInstance(); 405 } 406 @Override 407 public String toString() { 408 return "Current JSF FacesContext"; 409 } 410 }); 411 beanFactory.registerResolvableDependency(ExternalContext.class, new ObjectFactory<ExternalContext>() { 412 @Override 413 public ExternalContext getObject() { 414 return FacesContext.getCurrentInstance().getExternalContext(); 415 } 416 @Override 417 public String toString() { 418 return "Current JSF ExternalContext"; 419 } 420 }); 421 } 422 } 423 424}