001/* 002 * Copyright 2002-2018 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.servlet; 018 019import java.io.IOException; 020import java.security.Principal; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.List; 024import java.util.concurrent.Callable; 025import java.util.stream.Collectors; 026 027import javax.servlet.DispatcherType; 028import javax.servlet.ServletContext; 029import javax.servlet.ServletException; 030import javax.servlet.http.HttpServletRequest; 031import javax.servlet.http.HttpServletResponse; 032import javax.servlet.http.HttpServletResponseWrapper; 033 034import org.springframework.beans.BeanUtils; 035import org.springframework.context.ApplicationContext; 036import org.springframework.context.ApplicationContextAware; 037import org.springframework.context.ApplicationContextException; 038import org.springframework.context.ApplicationContextInitializer; 039import org.springframework.context.ApplicationListener; 040import org.springframework.context.ConfigurableApplicationContext; 041import org.springframework.context.event.ContextRefreshedEvent; 042import org.springframework.context.event.SourceFilteringListener; 043import org.springframework.context.i18n.LocaleContext; 044import org.springframework.context.i18n.LocaleContextHolder; 045import org.springframework.context.i18n.SimpleLocaleContext; 046import org.springframework.core.GenericTypeResolver; 047import org.springframework.core.annotation.AnnotationAwareOrderComparator; 048import org.springframework.core.env.ConfigurableEnvironment; 049import org.springframework.http.HttpMethod; 050import org.springframework.http.HttpStatus; 051import org.springframework.lang.Nullable; 052import org.springframework.util.ClassUtils; 053import org.springframework.util.ObjectUtils; 054import org.springframework.util.StringUtils; 055import org.springframework.web.context.ConfigurableWebApplicationContext; 056import org.springframework.web.context.ConfigurableWebEnvironment; 057import org.springframework.web.context.ContextLoader; 058import org.springframework.web.context.WebApplicationContext; 059import org.springframework.web.context.request.NativeWebRequest; 060import org.springframework.web.context.request.RequestAttributes; 061import org.springframework.web.context.request.RequestContextHolder; 062import org.springframework.web.context.request.ServletRequestAttributes; 063import org.springframework.web.context.request.async.CallableProcessingInterceptor; 064import org.springframework.web.context.request.async.WebAsyncManager; 065import org.springframework.web.context.request.async.WebAsyncUtils; 066import org.springframework.web.context.support.ServletRequestHandledEvent; 067import org.springframework.web.context.support.WebApplicationContextUtils; 068import org.springframework.web.context.support.XmlWebApplicationContext; 069import org.springframework.web.cors.CorsUtils; 070import org.springframework.web.util.NestedServletException; 071import org.springframework.web.util.WebUtils; 072 073/** 074 * Base servlet for Spring's web framework. Provides integration with 075 * a Spring application context, in a JavaBean-based overall solution. 076 * 077 * <p>This class offers the following functionality: 078 * <ul> 079 * <li>Manages a {@link org.springframework.web.context.WebApplicationContext 080 * WebApplicationContext} instance per servlet. The servlet's configuration is determined 081 * by beans in the servlet's namespace. 082 * <li>Publishes events on request processing, whether or not a request is 083 * successfully handled. 084 * </ul> 085 * 086 * <p>Subclasses must implement {@link #doService} to handle requests. Because this extends 087 * {@link HttpServletBean} rather than HttpServlet directly, bean properties are 088 * automatically mapped onto it. Subclasses can override {@link #initFrameworkServlet()} 089 * for custom initialization. 090 * 091 * <p>Detects a "contextClass" parameter at the servlet init-param level, 092 * falling back to the default context class, 093 * {@link org.springframework.web.context.support.XmlWebApplicationContext 094 * XmlWebApplicationContext}, if not found. Note that, with the default 095 * {@code FrameworkServlet}, a custom context class needs to implement the 096 * {@link org.springframework.web.context.ConfigurableWebApplicationContext 097 * ConfigurableWebApplicationContext} SPI. 098 * 099 * <p>Accepts an optional "contextInitializerClasses" servlet init-param that 100 * specifies one or more {@link org.springframework.context.ApplicationContextInitializer 101 * ApplicationContextInitializer} classes. The managed web application context will be 102 * delegated to these initializers, allowing for additional programmatic configuration, 103 * e.g. adding property sources or activating profiles against the {@linkplain 104 * org.springframework.context.ConfigurableApplicationContext#getEnvironment() context's 105 * environment}. See also {@link org.springframework.web.context.ContextLoader} which 106 * supports a "contextInitializerClasses" context-param with identical semantics for 107 * the "root" web application context. 108 * 109 * <p>Passes a "contextConfigLocation" servlet init-param to the context instance, 110 * parsing it into potentially multiple file paths which can be separated by any 111 * number of commas and spaces, like "test-servlet.xml, myServlet.xml". 112 * If not explicitly specified, the context implementation is supposed to build a 113 * default location from the namespace of the servlet. 114 * 115 * <p>Note: In case of multiple config locations, later bean definitions will 116 * override ones defined in earlier loaded files, at least when using Spring's 117 * default ApplicationContext implementation. This can be leveraged to 118 * deliberately override certain bean definitions via an extra XML file. 119 * 120 * <p>The default namespace is "'servlet-name'-servlet", e.g. "test-servlet" for a 121 * servlet-name "test" (leading to a "/WEB-INF/test-servlet.xml" default location 122 * with XmlWebApplicationContext). The namespace can also be set explicitly via 123 * the "namespace" servlet init-param. 124 * 125 * <p>As of Spring 3.1, {@code FrameworkServlet} may now be injected with a web 126 * application context, rather than creating its own internally. This is useful in Servlet 127 * 3.0+ environments, which support programmatic registration of servlet instances. See 128 * {@link #FrameworkServlet(WebApplicationContext)} Javadoc for details. 129 * 130 * @author Rod Johnson 131 * @author Juergen Hoeller 132 * @author Sam Brannen 133 * @author Chris Beams 134 * @author Rossen Stoyanchev 135 * @author Phillip Webb 136 * @see #doService 137 * @see #setContextClass 138 * @see #setContextConfigLocation 139 * @see #setContextInitializerClasses 140 * @see #setNamespace 141 */ 142@SuppressWarnings("serial") 143public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { 144 145 /** 146 * Suffix for WebApplicationContext namespaces. If a servlet of this class is 147 * given the name "test" in a context, the namespace used by the servlet will 148 * resolve to "test-servlet". 149 */ 150 public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet"; 151 152 /** 153 * Default context class for FrameworkServlet. 154 * @see org.springframework.web.context.support.XmlWebApplicationContext 155 */ 156 public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class; 157 158 /** 159 * Prefix for the ServletContext attribute for the WebApplicationContext. 160 * The completion is the servlet name. 161 */ 162 public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT."; 163 164 /** 165 * Any number of these characters are considered delimiters between 166 * multiple values in a single init-param String value. 167 */ 168 private static final String INIT_PARAM_DELIMITERS = ",; \t\n"; 169 170 171 /** ServletContext attribute to find the WebApplicationContext in. */ 172 @Nullable 173 private String contextAttribute; 174 175 /** WebApplicationContext implementation class to create. */ 176 private Class<?> contextClass = DEFAULT_CONTEXT_CLASS; 177 178 /** WebApplicationContext id to assign. */ 179 @Nullable 180 private String contextId; 181 182 /** Namespace for this servlet. */ 183 @Nullable 184 private String namespace; 185 186 /** Explicit context config location. */ 187 @Nullable 188 private String contextConfigLocation; 189 190 /** Actual ApplicationContextInitializer instances to apply to the context. */ 191 private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers = 192 new ArrayList<>(); 193 194 /** Comma-delimited ApplicationContextInitializer class names set through init param. */ 195 @Nullable 196 private String contextInitializerClasses; 197 198 /** Should we publish the context as a ServletContext attribute?. */ 199 private boolean publishContext = true; 200 201 /** Should we publish a ServletRequestHandledEvent at the end of each request?. */ 202 private boolean publishEvents = true; 203 204 /** Expose LocaleContext and RequestAttributes as inheritable for child threads?. */ 205 private boolean threadContextInheritable = false; 206 207 /** Should we dispatch an HTTP OPTIONS request to {@link #doService}?. */ 208 private boolean dispatchOptionsRequest = false; 209 210 /** Should we dispatch an HTTP TRACE request to {@link #doService}?. */ 211 private boolean dispatchTraceRequest = false; 212 213 /** Whether to log potentially sensitive info (request params at DEBUG + headers at TRACE). */ 214 private boolean enableLoggingRequestDetails = false; 215 216 /** WebApplicationContext for this servlet. */ 217 @Nullable 218 private WebApplicationContext webApplicationContext; 219 220 /** If the WebApplicationContext was injected via {@link #setApplicationContext}. */ 221 private boolean webApplicationContextInjected = false; 222 223 /** Flag used to detect whether onRefresh has already been called. */ 224 private volatile boolean refreshEventReceived = false; 225 226 /** Monitor for synchronized onRefresh execution. */ 227 private final Object onRefreshMonitor = new Object(); 228 229 230 /** 231 * Create a new {@code FrameworkServlet} that will create its own internal web 232 * application context based on defaults and values provided through servlet 233 * init-params. Typically used in Servlet 2.5 or earlier environments, where the only 234 * option for servlet registration is through {@code web.xml} which requires the use 235 * of a no-arg constructor. 236 * <p>Calling {@link #setContextConfigLocation} (init-param 'contextConfigLocation') 237 * will dictate which XML files will be loaded by the 238 * {@linkplain #DEFAULT_CONTEXT_CLASS default XmlWebApplicationContext} 239 * <p>Calling {@link #setContextClass} (init-param 'contextClass') overrides the 240 * default {@code XmlWebApplicationContext} and allows for specifying an alternative class, 241 * such as {@code AnnotationConfigWebApplicationContext}. 242 * <p>Calling {@link #setContextInitializerClasses} (init-param 'contextInitializerClasses') 243 * indicates which {@link ApplicationContextInitializer} classes should be used to 244 * further configure the internal application context prior to refresh(). 245 * @see #FrameworkServlet(WebApplicationContext) 246 */ 247 public FrameworkServlet() { 248 } 249 250 /** 251 * Create a new {@code FrameworkServlet} with the given web application context. This 252 * constructor is useful in Servlet 3.0+ environments where instance-based registration 253 * of servlets is possible through the {@link ServletContext#addServlet} API. 254 * <p>Using this constructor indicates that the following properties / init-params 255 * will be ignored: 256 * <ul> 257 * <li>{@link #setContextClass(Class)} / 'contextClass'</li> 258 * <li>{@link #setContextConfigLocation(String)} / 'contextConfigLocation'</li> 259 * <li>{@link #setContextAttribute(String)} / 'contextAttribute'</li> 260 * <li>{@link #setNamespace(String)} / 'namespace'</li> 261 * </ul> 262 * <p>The given web application context may or may not yet be {@linkplain 263 * ConfigurableApplicationContext#refresh() refreshed}. If it (a) is an implementation 264 * of {@link ConfigurableWebApplicationContext} and (b) has <strong>not</strong> 265 * already been refreshed (the recommended approach), then the following will occur: 266 * <ul> 267 * <li>If the given context does not already have a {@linkplain 268 * ConfigurableApplicationContext#setParent parent}, the root application context 269 * will be set as the parent.</li> 270 * <li>If the given context has not already been assigned an {@linkplain 271 * ConfigurableApplicationContext#setId id}, one will be assigned to it</li> 272 * <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to 273 * the application context</li> 274 * <li>{@link #postProcessWebApplicationContext} will be called</li> 275 * <li>Any {@link ApplicationContextInitializer ApplicationContextInitializers} specified through the 276 * "contextInitializerClasses" init-param or through the {@link 277 * #setContextInitializers} property will be applied.</li> 278 * <li>{@link ConfigurableApplicationContext#refresh refresh()} will be called</li> 279 * </ul> 280 * If the context has already been refreshed or does not implement 281 * {@code ConfigurableWebApplicationContext}, none of the above will occur under the 282 * assumption that the user has performed these actions (or not) per his or her 283 * specific needs. 284 * <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples. 285 * @param webApplicationContext the context to use 286 * @see #initWebApplicationContext 287 * @see #configureAndRefreshWebApplicationContext 288 * @see org.springframework.web.WebApplicationInitializer 289 */ 290 public FrameworkServlet(WebApplicationContext webApplicationContext) { 291 this.webApplicationContext = webApplicationContext; 292 } 293 294 295 /** 296 * Set the name of the ServletContext attribute which should be used to retrieve the 297 * {@link WebApplicationContext} that this servlet is supposed to use. 298 */ 299 public void setContextAttribute(@Nullable String contextAttribute) { 300 this.contextAttribute = contextAttribute; 301 } 302 303 /** 304 * Return the name of the ServletContext attribute which should be used to retrieve the 305 * {@link WebApplicationContext} that this servlet is supposed to use. 306 */ 307 @Nullable 308 public String getContextAttribute() { 309 return this.contextAttribute; 310 } 311 312 /** 313 * Set a custom context class. This class must be of type 314 * {@link org.springframework.web.context.WebApplicationContext}. 315 * <p>When using the default FrameworkServlet implementation, 316 * the context class must also implement the 317 * {@link org.springframework.web.context.ConfigurableWebApplicationContext} 318 * interface. 319 * @see #createWebApplicationContext 320 */ 321 public void setContextClass(Class<?> contextClass) { 322 this.contextClass = contextClass; 323 } 324 325 /** 326 * Return the custom context class. 327 */ 328 public Class<?> getContextClass() { 329 return this.contextClass; 330 } 331 332 /** 333 * Specify a custom WebApplicationContext id, 334 * to be used as serialization id for the underlying BeanFactory. 335 */ 336 public void setContextId(@Nullable String contextId) { 337 this.contextId = contextId; 338 } 339 340 /** 341 * Return the custom WebApplicationContext id, if any. 342 */ 343 @Nullable 344 public String getContextId() { 345 return this.contextId; 346 } 347 348 /** 349 * Set a custom namespace for this servlet, 350 * to be used for building a default context config location. 351 */ 352 public void setNamespace(String namespace) { 353 this.namespace = namespace; 354 } 355 356 /** 357 * Return the namespace for this servlet, falling back to default scheme if 358 * no custom namespace was set: e.g. "test-servlet" for a servlet named "test". 359 */ 360 public String getNamespace() { 361 return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX); 362 } 363 364 /** 365 * Set the context config location explicitly, instead of relying on the default 366 * location built from the namespace. This location string can consist of 367 * multiple locations separated by any number of commas and spaces. 368 */ 369 public void setContextConfigLocation(@Nullable String contextConfigLocation) { 370 this.contextConfigLocation = contextConfigLocation; 371 } 372 373 /** 374 * Return the explicit context config location, if any. 375 */ 376 @Nullable 377 public String getContextConfigLocation() { 378 return this.contextConfigLocation; 379 } 380 381 /** 382 * Specify which {@link ApplicationContextInitializer} instances should be used 383 * to initialize the application context used by this {@code FrameworkServlet}. 384 * @see #configureAndRefreshWebApplicationContext 385 * @see #applyInitializers 386 */ 387 @SuppressWarnings("unchecked") 388 public void setContextInitializers(@Nullable ApplicationContextInitializer<?>... initializers) { 389 if (initializers != null) { 390 for (ApplicationContextInitializer<?> initializer : initializers) { 391 this.contextInitializers.add((ApplicationContextInitializer<ConfigurableApplicationContext>) initializer); 392 } 393 } 394 } 395 396 /** 397 * Specify the set of fully-qualified {@link ApplicationContextInitializer} class 398 * names, per the optional "contextInitializerClasses" servlet init-param. 399 * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext) 400 * @see #applyInitializers(ConfigurableApplicationContext) 401 */ 402 public void setContextInitializerClasses(String contextInitializerClasses) { 403 this.contextInitializerClasses = contextInitializerClasses; 404 } 405 406 /** 407 * Set whether to publish this servlet's context as a ServletContext attribute, 408 * available to all objects in the web container. Default is "true". 409 * <p>This is especially handy during testing, although it is debatable whether 410 * it's good practice to let other application objects access the context this way. 411 */ 412 public void setPublishContext(boolean publishContext) { 413 this.publishContext = publishContext; 414 } 415 416 /** 417 * Set whether this servlet should publish a ServletRequestHandledEvent at the end 418 * of each request. Default is "true"; can be turned off for a slight performance 419 * improvement, provided that no ApplicationListeners rely on such events. 420 * @see org.springframework.web.context.support.ServletRequestHandledEvent 421 */ 422 public void setPublishEvents(boolean publishEvents) { 423 this.publishEvents = publishEvents; 424 } 425 426 /** 427 * Set whether to expose the LocaleContext and RequestAttributes as inheritable 428 * for child threads (using an {@link java.lang.InheritableThreadLocal}). 429 * <p>Default is "false", to avoid side effects on spawned background threads. 430 * Switch this to "true" to enable inheritance for custom child threads which 431 * are spawned during request processing and only used for this request 432 * (that is, ending after their initial task, without reuse of the thread). 433 * <p><b>WARNING:</b> Do not use inheritance for child threads if you are 434 * accessing a thread pool which is configured to potentially add new threads 435 * on demand (e.g. a JDK {@link java.util.concurrent.ThreadPoolExecutor}), 436 * since this will expose the inherited context to such a pooled thread. 437 */ 438 public void setThreadContextInheritable(boolean threadContextInheritable) { 439 this.threadContextInheritable = threadContextInheritable; 440 } 441 442 /** 443 * Set whether this servlet should dispatch an HTTP OPTIONS request to 444 * the {@link #doService} method. 445 * <p>Default in the {@code FrameworkServlet} is "false", applying 446 * {@link javax.servlet.http.HttpServlet}'s default behavior (i.e.enumerating 447 * all standard HTTP request methods as a response to the OPTIONS request). 448 * Note however that as of 4.3 the {@code DispatcherServlet} sets this 449 * property to "true" by default due to its built-in support for OPTIONS. 450 * <p>Turn this flag on if you prefer OPTIONS requests to go through the 451 * regular dispatching chain, just like other HTTP requests. This usually 452 * means that your controllers will receive those requests; make sure 453 * that those endpoints are actually able to handle an OPTIONS request. 454 * <p>Note that HttpServlet's default OPTIONS processing will be applied 455 * in any case if your controllers happen to not set the 'Allow' header 456 * (as required for an OPTIONS response). 457 */ 458 public void setDispatchOptionsRequest(boolean dispatchOptionsRequest) { 459 this.dispatchOptionsRequest = dispatchOptionsRequest; 460 } 461 462 /** 463 * Set whether this servlet should dispatch an HTTP TRACE request to 464 * the {@link #doService} method. 465 * <p>Default is "false", applying {@link javax.servlet.http.HttpServlet}'s 466 * default behavior (i.e. reflecting the message received back to the client). 467 * <p>Turn this flag on if you prefer TRACE requests to go through the 468 * regular dispatching chain, just like other HTTP requests. This usually 469 * means that your controllers will receive those requests; make sure 470 * that those endpoints are actually able to handle a TRACE request. 471 * <p>Note that HttpServlet's default TRACE processing will be applied 472 * in any case if your controllers happen to not generate a response 473 * of content type 'message/http' (as required for a TRACE response). 474 */ 475 public void setDispatchTraceRequest(boolean dispatchTraceRequest) { 476 this.dispatchTraceRequest = dispatchTraceRequest; 477 } 478 479 /** 480 * Whether to log request params at DEBUG level, and headers at TRACE level. 481 * Both may contain sensitive information. 482 * <p>By default set to {@code false} so that request details are not shown. 483 * @param enable whether to enable or not 484 * @since 5.1 485 */ 486 public void setEnableLoggingRequestDetails(boolean enable) { 487 this.enableLoggingRequestDetails = enable; 488 } 489 490 /** 491 * Whether logging of potentially sensitive, request details at DEBUG and 492 * TRACE level is allowed. 493 * @since 5.1 494 */ 495 public boolean isEnableLoggingRequestDetails() { 496 return this.enableLoggingRequestDetails; 497 } 498 499 /** 500 * Called by Spring via {@link ApplicationContextAware} to inject the current 501 * application context. This method allows FrameworkServlets to be registered as 502 * Spring beans inside an existing {@link WebApplicationContext} rather than 503 * {@link #findWebApplicationContext() finding} a 504 * {@link org.springframework.web.context.ContextLoaderListener bootstrapped} context. 505 * <p>Primarily added to support use in embedded servlet containers. 506 * @since 4.0 507 */ 508 @Override 509 public void setApplicationContext(ApplicationContext applicationContext) { 510 if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) { 511 this.webApplicationContext = (WebApplicationContext) applicationContext; 512 this.webApplicationContextInjected = true; 513 } 514 } 515 516 517 /** 518 * Overridden method of {@link HttpServletBean}, invoked after any bean properties 519 * have been set. Creates this servlet's WebApplicationContext. 520 */ 521 @Override 522 protected final void initServletBean() throws ServletException { 523 getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); 524 if (logger.isInfoEnabled()) { 525 logger.info("Initializing Servlet '" + getServletName() + "'"); 526 } 527 long startTime = System.currentTimeMillis(); 528 529 try { 530 this.webApplicationContext = initWebApplicationContext(); 531 initFrameworkServlet(); 532 } 533 catch (ServletException | RuntimeException ex) { 534 logger.error("Context initialization failed", ex); 535 throw ex; 536 } 537 538 if (logger.isDebugEnabled()) { 539 String value = this.enableLoggingRequestDetails ? 540 "shown which may lead to unsafe logging of potentially sensitive data" : 541 "masked to prevent unsafe logging of potentially sensitive data"; 542 logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + 543 "': request parameters and headers will be " + value); 544 } 545 546 if (logger.isInfoEnabled()) { 547 logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); 548 } 549 } 550 551 /** 552 * Initialize and publish the WebApplicationContext for this servlet. 553 * <p>Delegates to {@link #createWebApplicationContext} for actual creation 554 * of the context. Can be overridden in subclasses. 555 * @return the WebApplicationContext instance 556 * @see #FrameworkServlet(WebApplicationContext) 557 * @see #setContextClass 558 * @see #setContextConfigLocation 559 */ 560 protected WebApplicationContext initWebApplicationContext() { 561 WebApplicationContext rootContext = 562 WebApplicationContextUtils.getWebApplicationContext(getServletContext()); 563 WebApplicationContext wac = null; 564 565 if (this.webApplicationContext != null) { 566 // A context instance was injected at construction time -> use it 567 wac = this.webApplicationContext; 568 if (wac instanceof ConfigurableWebApplicationContext) { 569 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; 570 if (!cwac.isActive()) { 571 // The context has not yet been refreshed -> provide services such as 572 // setting the parent context, setting the application context id, etc 573 if (cwac.getParent() == null) { 574 // The context instance was injected without an explicit parent -> set 575 // the root application context (if any; may be null) as the parent 576 cwac.setParent(rootContext); 577 } 578 configureAndRefreshWebApplicationContext(cwac); 579 } 580 } 581 } 582 if (wac == null) { 583 // No context instance was injected at construction time -> see if one 584 // has been registered in the servlet context. If one exists, it is assumed 585 // that the parent context (if any) has already been set and that the 586 // user has performed any initialization such as setting the context id 587 wac = findWebApplicationContext(); 588 } 589 if (wac == null) { 590 // No context instance is defined for this servlet -> create a local one 591 wac = createWebApplicationContext(rootContext); 592 } 593 594 if (!this.refreshEventReceived) { 595 // Either the context is not a ConfigurableApplicationContext with refresh 596 // support or the context injected at construction time had already been 597 // refreshed -> trigger initial onRefresh manually here. 598 synchronized (this.onRefreshMonitor) { 599 onRefresh(wac); 600 } 601 } 602 603 if (this.publishContext) { 604 // Publish the context as a servlet context attribute. 605 String attrName = getServletContextAttributeName(); 606 getServletContext().setAttribute(attrName, wac); 607 } 608 609 return wac; 610 } 611 612 /** 613 * Retrieve a {@code WebApplicationContext} from the {@code ServletContext} 614 * attribute with the {@link #setContextAttribute configured name}. The 615 * {@code WebApplicationContext} must have already been loaded and stored in the 616 * {@code ServletContext} before this servlet gets initialized (or invoked). 617 * <p>Subclasses may override this method to provide a different 618 * {@code WebApplicationContext} retrieval strategy. 619 * @return the WebApplicationContext for this servlet, or {@code null} if not found 620 * @see #getContextAttribute() 621 */ 622 @Nullable 623 protected WebApplicationContext findWebApplicationContext() { 624 String attrName = getContextAttribute(); 625 if (attrName == null) { 626 return null; 627 } 628 WebApplicationContext wac = 629 WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); 630 if (wac == null) { 631 throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); 632 } 633 return wac; 634 } 635 636 /** 637 * Instantiate the WebApplicationContext for this servlet, either a default 638 * {@link org.springframework.web.context.support.XmlWebApplicationContext} 639 * or a {@link #setContextClass custom context class}, if set. 640 * <p>This implementation expects custom contexts to implement the 641 * {@link org.springframework.web.context.ConfigurableWebApplicationContext} 642 * interface. Can be overridden in subclasses. 643 * <p>Do not forget to register this servlet instance as application listener on the 644 * created context (for triggering its {@link #onRefresh callback}, and to call 645 * {@link org.springframework.context.ConfigurableApplicationContext#refresh()} 646 * before returning the context instance. 647 * @param parent the parent ApplicationContext to use, or {@code null} if none 648 * @return the WebApplicationContext for this servlet 649 * @see org.springframework.web.context.support.XmlWebApplicationContext 650 */ 651 protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { 652 Class<?> contextClass = getContextClass(); 653 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 654 throw new ApplicationContextException( 655 "Fatal initialization error in servlet with name '" + getServletName() + 656 "': custom WebApplicationContext class [" + contextClass.getName() + 657 "] is not of type ConfigurableWebApplicationContext"); 658 } 659 ConfigurableWebApplicationContext wac = 660 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 661 662 wac.setEnvironment(getEnvironment()); 663 wac.setParent(parent); 664 String configLocation = getContextConfigLocation(); 665 if (configLocation != null) { 666 wac.setConfigLocation(configLocation); 667 } 668 configureAndRefreshWebApplicationContext(wac); 669 670 return wac; 671 } 672 673 protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { 674 if (ObjectUtils.identityToString(wac).equals(wac.getId())) { 675 // The application context id is still set to its original default value 676 // -> assign a more useful id based on available information 677 if (this.contextId != null) { 678 wac.setId(this.contextId); 679 } 680 else { 681 // Generate default id... 682 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + 683 ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); 684 } 685 } 686 687 wac.setServletContext(getServletContext()); 688 wac.setServletConfig(getServletConfig()); 689 wac.setNamespace(getNamespace()); 690 wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); 691 692 // The wac environment's #initPropertySources will be called in any case when the context 693 // is refreshed; do it eagerly here to ensure servlet property sources are in place for 694 // use in any post-processing or initialization that occurs below prior to #refresh 695 ConfigurableEnvironment env = wac.getEnvironment(); 696 if (env instanceof ConfigurableWebEnvironment) { 697 ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); 698 } 699 700 postProcessWebApplicationContext(wac); 701 applyInitializers(wac); 702 wac.refresh(); 703 } 704 705 /** 706 * Instantiate the WebApplicationContext for this servlet, either a default 707 * {@link org.springframework.web.context.support.XmlWebApplicationContext} 708 * or a {@link #setContextClass custom context class}, if set. 709 * Delegates to #createWebApplicationContext(ApplicationContext). 710 * @param parent the parent WebApplicationContext to use, or {@code null} if none 711 * @return the WebApplicationContext for this servlet 712 * @see org.springframework.web.context.support.XmlWebApplicationContext 713 * @see #createWebApplicationContext(ApplicationContext) 714 */ 715 protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) { 716 return createWebApplicationContext((ApplicationContext) parent); 717 } 718 719 /** 720 * Post-process the given WebApplicationContext before it is refreshed 721 * and activated as context for this servlet. 722 * <p>The default implementation is empty. {@code refresh()} will 723 * be called automatically after this method returns. 724 * <p>Note that this method is designed to allow subclasses to modify the application 725 * context, while {@link #initWebApplicationContext} is designed to allow 726 * end-users to modify the context through the use of 727 * {@link ApplicationContextInitializer ApplicationContextInitializers}. 728 * @param wac the configured WebApplicationContext (not refreshed yet) 729 * @see #createWebApplicationContext 730 * @see #initWebApplicationContext 731 * @see ConfigurableWebApplicationContext#refresh() 732 */ 733 protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) { 734 } 735 736 /** 737 * Delegate the WebApplicationContext before it is refreshed to any 738 * {@link ApplicationContextInitializer} instances specified by the 739 * "contextInitializerClasses" servlet init-param. 740 * <p>See also {@link #postProcessWebApplicationContext}, which is designed to allow 741 * subclasses (as opposed to end-users) to modify the application context, and is 742 * called immediately before this method. 743 * @param wac the configured WebApplicationContext (not refreshed yet) 744 * @see #createWebApplicationContext 745 * @see #postProcessWebApplicationContext 746 * @see ConfigurableApplicationContext#refresh() 747 */ 748 protected void applyInitializers(ConfigurableApplicationContext wac) { 749 String globalClassNames = getServletContext().getInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM); 750 if (globalClassNames != null) { 751 for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) { 752 this.contextInitializers.add(loadInitializer(className, wac)); 753 } 754 } 755 756 if (this.contextInitializerClasses != null) { 757 for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS)) { 758 this.contextInitializers.add(loadInitializer(className, wac)); 759 } 760 } 761 762 AnnotationAwareOrderComparator.sort(this.contextInitializers); 763 for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) { 764 initializer.initialize(wac); 765 } 766 } 767 768 @SuppressWarnings("unchecked") 769 private ApplicationContextInitializer<ConfigurableApplicationContext> loadInitializer( 770 String className, ConfigurableApplicationContext wac) { 771 try { 772 Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader()); 773 Class<?> initializerContextClass = 774 GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); 775 if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) { 776 throw new ApplicationContextException(String.format( 777 "Could not apply context initializer [%s] since its generic parameter [%s] " + 778 "is not assignable from the type of application context used by this " + 779 "framework servlet: [%s]", initializerClass.getName(), initializerContextClass.getName(), 780 wac.getClass().getName())); 781 } 782 return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class); 783 } 784 catch (ClassNotFoundException ex) { 785 throw new ApplicationContextException(String.format("Could not load class [%s] specified " + 786 "via 'contextInitializerClasses' init-param", className), ex); 787 } 788 } 789 790 /** 791 * Return the ServletContext attribute name for this servlet's WebApplicationContext. 792 * <p>The default implementation returns 793 * {@code SERVLET_CONTEXT_PREFIX + servlet name}. 794 * @see #SERVLET_CONTEXT_PREFIX 795 * @see #getServletName 796 */ 797 public String getServletContextAttributeName() { 798 return SERVLET_CONTEXT_PREFIX + getServletName(); 799 } 800 801 /** 802 * Return this servlet's WebApplicationContext. 803 */ 804 @Nullable 805 public final WebApplicationContext getWebApplicationContext() { 806 return this.webApplicationContext; 807 } 808 809 810 /** 811 * This method will be invoked after any bean properties have been set and 812 * the WebApplicationContext has been loaded. The default implementation is empty; 813 * subclasses may override this method to perform any initialization they require. 814 * @throws ServletException in case of an initialization exception 815 */ 816 protected void initFrameworkServlet() throws ServletException { 817 } 818 819 /** 820 * Refresh this servlet's application context, as well as the 821 * dependent state of the servlet. 822 * @see #getWebApplicationContext() 823 * @see org.springframework.context.ConfigurableApplicationContext#refresh() 824 */ 825 public void refresh() { 826 WebApplicationContext wac = getWebApplicationContext(); 827 if (!(wac instanceof ConfigurableApplicationContext)) { 828 throw new IllegalStateException("WebApplicationContext does not support refresh: " + wac); 829 } 830 ((ConfigurableApplicationContext) wac).refresh(); 831 } 832 833 /** 834 * Callback that receives refresh events from this servlet's WebApplicationContext. 835 * <p>The default implementation calls {@link #onRefresh}, 836 * triggering a refresh of this servlet's context-dependent state. 837 * @param event the incoming ApplicationContext event 838 */ 839 public void onApplicationEvent(ContextRefreshedEvent event) { 840 this.refreshEventReceived = true; 841 synchronized (this.onRefreshMonitor) { 842 onRefresh(event.getApplicationContext()); 843 } 844 } 845 846 /** 847 * Template method which can be overridden to add servlet-specific refresh work. 848 * Called after successful context refresh. 849 * <p>This implementation is empty. 850 * @param context the current WebApplicationContext 851 * @see #refresh() 852 */ 853 protected void onRefresh(ApplicationContext context) { 854 // For subclasses: do nothing by default. 855 } 856 857 /** 858 * Close the WebApplicationContext of this servlet. 859 * @see org.springframework.context.ConfigurableApplicationContext#close() 860 */ 861 @Override 862 public void destroy() { 863 getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'"); 864 // Only call close() on WebApplicationContext if locally managed... 865 if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) { 866 ((ConfigurableApplicationContext) this.webApplicationContext).close(); 867 } 868 } 869 870 871 /** 872 * Override the parent class implementation in order to intercept PATCH requests. 873 */ 874 @Override 875 protected void service(HttpServletRequest request, HttpServletResponse response) 876 throws ServletException, IOException { 877 878 HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); 879 if (httpMethod == HttpMethod.PATCH || httpMethod == null) { 880 processRequest(request, response); 881 } 882 else { 883 super.service(request, response); 884 } 885 } 886 887 /** 888 * Delegate GET requests to processRequest/doService. 889 * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead}, 890 * with a {@code NoBodyResponse} that just captures the content length. 891 * @see #doService 892 * @see #doHead 893 */ 894 @Override 895 protected final void doGet(HttpServletRequest request, HttpServletResponse response) 896 throws ServletException, IOException { 897 898 processRequest(request, response); 899 } 900 901 /** 902 * Delegate POST requests to {@link #processRequest}. 903 * @see #doService 904 */ 905 @Override 906 protected final void doPost(HttpServletRequest request, HttpServletResponse response) 907 throws ServletException, IOException { 908 909 processRequest(request, response); 910 } 911 912 /** 913 * Delegate PUT requests to {@link #processRequest}. 914 * @see #doService 915 */ 916 @Override 917 protected final void doPut(HttpServletRequest request, HttpServletResponse response) 918 throws ServletException, IOException { 919 920 processRequest(request, response); 921 } 922 923 /** 924 * Delegate DELETE requests to {@link #processRequest}. 925 * @see #doService 926 */ 927 @Override 928 protected final void doDelete(HttpServletRequest request, HttpServletResponse response) 929 throws ServletException, IOException { 930 931 processRequest(request, response); 932 } 933 934 /** 935 * Delegate OPTIONS requests to {@link #processRequest}, if desired. 936 * <p>Applies HttpServlet's standard OPTIONS processing otherwise, 937 * and also if there is still no 'Allow' header set after dispatching. 938 * @see #doService 939 */ 940 @Override 941 protected void doOptions(HttpServletRequest request, HttpServletResponse response) 942 throws ServletException, IOException { 943 944 if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) { 945 processRequest(request, response); 946 if (response.containsHeader("Allow")) { 947 // Proper OPTIONS response coming from a handler - we're done. 948 return; 949 } 950 } 951 952 // Use response wrapper in order to always add PATCH to the allowed methods 953 super.doOptions(request, new HttpServletResponseWrapper(response) { 954 @Override 955 public void setHeader(String name, String value) { 956 if ("Allow".equals(name)) { 957 value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name(); 958 } 959 super.setHeader(name, value); 960 } 961 }); 962 } 963 964 /** 965 * Delegate TRACE requests to {@link #processRequest}, if desired. 966 * <p>Applies HttpServlet's standard TRACE processing otherwise. 967 * @see #doService 968 */ 969 @Override 970 protected void doTrace(HttpServletRequest request, HttpServletResponse response) 971 throws ServletException, IOException { 972 973 if (this.dispatchTraceRequest) { 974 processRequest(request, response); 975 if ("message/http".equals(response.getContentType())) { 976 // Proper TRACE response coming from a handler - we're done. 977 return; 978 } 979 } 980 super.doTrace(request, response); 981 } 982 983 /** 984 * Process this request, publishing an event regardless of the outcome. 985 * <p>The actual event handling is performed by the abstract 986 * {@link #doService} template method. 987 */ 988 protected final void processRequest(HttpServletRequest request, HttpServletResponse response) 989 throws ServletException, IOException { 990 991 long startTime = System.currentTimeMillis(); 992 Throwable failureCause = null; 993 994 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); 995 LocaleContext localeContext = buildLocaleContext(request); 996 997 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); 998 ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); 999 1000 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 1001 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); 1002 1003 initContextHolders(request, localeContext, requestAttributes); 1004 1005 try { 1006 doService(request, response); 1007 } 1008 catch (ServletException | IOException ex) { 1009 failureCause = ex; 1010 throw ex; 1011 } 1012 catch (Throwable ex) { 1013 failureCause = ex; 1014 throw new NestedServletException("Request processing failed", ex); 1015 } 1016 1017 finally { 1018 resetContextHolders(request, previousLocaleContext, previousAttributes); 1019 if (requestAttributes != null) { 1020 requestAttributes.requestCompleted(); 1021 } 1022 logResult(request, response, failureCause, asyncManager); 1023 publishRequestHandledEvent(request, response, startTime, failureCause); 1024 } 1025 } 1026 1027 /** 1028 * Build a LocaleContext for the given request, exposing the request's 1029 * primary locale as current locale. 1030 * @param request current HTTP request 1031 * @return the corresponding LocaleContext, or {@code null} if none to bind 1032 * @see LocaleContextHolder#setLocaleContext 1033 */ 1034 @Nullable 1035 protected LocaleContext buildLocaleContext(HttpServletRequest request) { 1036 return new SimpleLocaleContext(request.getLocale()); 1037 } 1038 1039 /** 1040 * Build ServletRequestAttributes for the given request (potentially also 1041 * holding a reference to the response), taking pre-bound attributes 1042 * (and their type) into consideration. 1043 * @param request current HTTP request 1044 * @param response current HTTP response 1045 * @param previousAttributes pre-bound RequestAttributes instance, if any 1046 * @return the ServletRequestAttributes to bind, or {@code null} to preserve 1047 * the previously bound instance (or not binding any, if none bound before) 1048 * @see RequestContextHolder#setRequestAttributes 1049 */ 1050 @Nullable 1051 protected ServletRequestAttributes buildRequestAttributes(HttpServletRequest request, 1052 @Nullable HttpServletResponse response, @Nullable RequestAttributes previousAttributes) { 1053 1054 if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) { 1055 return new ServletRequestAttributes(request, response); 1056 } 1057 else { 1058 return null; // preserve the pre-bound RequestAttributes instance 1059 } 1060 } 1061 1062 private void initContextHolders(HttpServletRequest request, 1063 @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) { 1064 1065 if (localeContext != null) { 1066 LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable); 1067 } 1068 if (requestAttributes != null) { 1069 RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); 1070 } 1071 } 1072 1073 private void resetContextHolders(HttpServletRequest request, 1074 @Nullable LocaleContext prevLocaleContext, @Nullable RequestAttributes previousAttributes) { 1075 1076 LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable); 1077 RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable); 1078 } 1079 1080 private void logResult(HttpServletRequest request, HttpServletResponse response, 1081 @Nullable Throwable failureCause, WebAsyncManager asyncManager) { 1082 1083 if (!logger.isDebugEnabled()) { 1084 return; 1085 } 1086 1087 String dispatchType = request.getDispatcherType().name(); 1088 boolean initialDispatch = request.getDispatcherType().equals(DispatcherType.REQUEST); 1089 1090 if (failureCause != null) { 1091 if (!initialDispatch) { 1092 // FORWARD/ERROR/ASYNC: minimal message (there should be enough context already) 1093 if (logger.isDebugEnabled()) { 1094 logger.debug("Unresolved failure from \"" + dispatchType + "\" dispatch: " + failureCause); 1095 } 1096 } 1097 else if (logger.isTraceEnabled()) { 1098 logger.trace("Failed to complete request", failureCause); 1099 } 1100 else { 1101 logger.debug("Failed to complete request: " + failureCause); 1102 } 1103 return; 1104 } 1105 1106 if (asyncManager.isConcurrentHandlingStarted()) { 1107 logger.debug("Exiting but response remains open for further handling"); 1108 return; 1109 } 1110 1111 int status = response.getStatus(); 1112 String headers = ""; // nothing below trace 1113 1114 if (logger.isTraceEnabled()) { 1115 Collection<String> names = response.getHeaderNames(); 1116 if (this.enableLoggingRequestDetails) { 1117 headers = names.stream().map(name -> name + ":" + response.getHeaders(name)) 1118 .collect(Collectors.joining(", ")); 1119 } 1120 else { 1121 headers = names.isEmpty() ? "" : "masked"; 1122 } 1123 headers = ", headers={" + headers + "}"; 1124 } 1125 1126 if (!initialDispatch) { 1127 logger.debug("Exiting from \"" + dispatchType + "\" dispatch, status " + status + headers); 1128 } 1129 else { 1130 HttpStatus httpStatus = HttpStatus.resolve(status); 1131 logger.debug("Completed " + (httpStatus != null ? httpStatus : status) + headers); 1132 } 1133 } 1134 1135 private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response, 1136 long startTime, @Nullable Throwable failureCause) { 1137 1138 if (this.publishEvents && this.webApplicationContext != null) { 1139 // Whether or not we succeeded, publish an event. 1140 long processingTime = System.currentTimeMillis() - startTime; 1141 this.webApplicationContext.publishEvent( 1142 new ServletRequestHandledEvent(this, 1143 request.getRequestURI(), request.getRemoteAddr(), 1144 request.getMethod(), getServletConfig().getServletName(), 1145 WebUtils.getSessionId(request), getUsernameForRequest(request), 1146 processingTime, failureCause, response.getStatus())); 1147 } 1148 } 1149 1150 /** 1151 * Determine the username for the given request. 1152 * <p>The default implementation takes the name of the UserPrincipal, if any. 1153 * Can be overridden in subclasses. 1154 * @param request current HTTP request 1155 * @return the username, or {@code null} if none found 1156 * @see javax.servlet.http.HttpServletRequest#getUserPrincipal() 1157 */ 1158 @Nullable 1159 protected String getUsernameForRequest(HttpServletRequest request) { 1160 Principal userPrincipal = request.getUserPrincipal(); 1161 return (userPrincipal != null ? userPrincipal.getName() : null); 1162 } 1163 1164 1165 /** 1166 * Subclasses must implement this method to do the work of request handling, 1167 * receiving a centralized callback for GET, POST, PUT and DELETE. 1168 * <p>The contract is essentially the same as that for the commonly overridden 1169 * {@code doGet} or {@code doPost} methods of HttpServlet. 1170 * <p>This class intercepts calls to ensure that exception handling and 1171 * event publication takes place. 1172 * @param request current HTTP request 1173 * @param response current HTTP response 1174 * @throws Exception in case of any kind of processing failure 1175 * @see javax.servlet.http.HttpServlet#doGet 1176 * @see javax.servlet.http.HttpServlet#doPost 1177 */ 1178 protected abstract void doService(HttpServletRequest request, HttpServletResponse response) 1179 throws Exception; 1180 1181 1182 /** 1183 * ApplicationListener endpoint that receives events from this servlet's WebApplicationContext 1184 * only, delegating to {@code onApplicationEvent} on the FrameworkServlet instance. 1185 */ 1186 private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> { 1187 1188 @Override 1189 public void onApplicationEvent(ContextRefreshedEvent event) { 1190 FrameworkServlet.this.onApplicationEvent(event); 1191 } 1192 } 1193 1194 1195 /** 1196 * CallableProcessingInterceptor implementation that initializes and resets 1197 * FrameworkServlet's context holders, i.e. LocaleContextHolder and RequestContextHolder. 1198 */ 1199 private class RequestBindingInterceptor implements CallableProcessingInterceptor { 1200 1201 @Override 1202 public <T> void preProcess(NativeWebRequest webRequest, Callable<T> task) { 1203 HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); 1204 if (request != null) { 1205 HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); 1206 initContextHolders(request, buildLocaleContext(request), 1207 buildRequestAttributes(request, response, null)); 1208 } 1209 } 1210 @Override 1211 public <T> void postProcess(NativeWebRequest webRequest, Callable<T> task, Object concurrentResult) { 1212 HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); 1213 if (request != null) { 1214 resetContextHolders(request, null, null); 1215 } 1216 } 1217 } 1218 1219}