2121import java .util .ArrayList ;
2222import java .util .Collections ;
2323import java .util .concurrent .Callable ;
24-
2524import javax .servlet .ServletContext ;
2625import javax .servlet .ServletException ;
2726import javax .servlet .http .HttpServletRequest ;
5251import org .springframework .web .context .request .RequestAttributes ;
5352import org .springframework .web .context .request .RequestContextHolder ;
5453import org .springframework .web .context .request .ServletRequestAttributes ;
55- import org .springframework .web .context .request .async .CallableProcessingInterceptor ;
5654import org .springframework .web .context .request .async .CallableProcessingInterceptorAdapter ;
5755import org .springframework .web .context .request .async .WebAsyncManager ;
5856import org .springframework .web .context .request .async .WebAsyncUtils ;
@@ -785,6 +783,18 @@ protected void onRefresh(ApplicationContext context) {
785783 // For subclasses: do nothing by default.
786784 }
787785
786+ /**
787+ * Close the WebApplicationContext of this servlet.
788+ * @see org.springframework.context.ConfigurableApplicationContext#close()
789+ */
790+ @ Override
791+ public void destroy () {
792+ getServletContext ().log ("Destroying Spring FrameworkServlet '" + getServletName () + "'" );
793+ if (this .webApplicationContext instanceof ConfigurableApplicationContext ) {
794+ ((ConfigurableApplicationContext ) this .webApplicationContext ).close ();
795+ }
796+ }
797+
788798
789799 /**
790800 * Override the parent class implementation in order to intercept PATCH
@@ -873,7 +883,7 @@ protected void doOptions(HttpServletRequest request, HttpServletResponse respons
873883 super .doOptions (request , new HttpServletResponseWrapper (response ) {
874884 @ Override
875885 public void setHeader (String name , String value ) {
876- if ("Allow" .equals (name )) {
886+ if ("Allow" .equals (name )) {
877887 value = (StringUtils .hasLength (value ) ? value + ", " : "" ) + RequestMethod .PATCH .name ();
878888 }
879889 super .setHeader (name , value );
@@ -915,15 +925,12 @@ protected final void processRequest(HttpServletRequest request, HttpServletRespo
915925 LocaleContext localeContext = buildLocaleContext (request );
916926
917927 RequestAttributes previousAttributes = RequestContextHolder .getRequestAttributes ();
918- ServletRequestAttributes requestAttributes = null ;
919- if (previousAttributes == null || (previousAttributes instanceof ServletRequestAttributes )) {
920- requestAttributes = new ServletRequestAttributes (request );
921- }
922-
923- initContextHolders (request , localeContext , requestAttributes );
928+ ServletRequestAttributes requestAttributes = buildRequestAttributes (request , response , previousAttributes );
924929
925930 WebAsyncManager asyncManager = WebAsyncUtils .getAsyncManager (request );
926- asyncManager .registerCallableInterceptor (FrameworkServlet .class .getName (), getRequestBindingInterceptor (request ));
931+ asyncManager .registerCallableInterceptor (FrameworkServlet .class .getName (), new RequestBindingInterceptor ());
932+
933+ initContextHolders (request , localeContext , requestAttributes );
927934
928935 try {
929936 doService (request , response );
@@ -950,46 +957,62 @@ protected final void processRequest(HttpServletRequest request, HttpServletRespo
950957 if (logger .isDebugEnabled ()) {
951958 if (failureCause != null ) {
952959 this .logger .debug ("Could not complete request" , failureCause );
953- } else {
960+ }
961+ else {
954962 if (asyncManager .isConcurrentHandlingStarted ()) {
955- if (logger .isDebugEnabled ()) {
956- logger .debug ("Leaving response open for concurrent processing" );
957- }
963+ logger .debug ("Leaving response open for concurrent processing" );
958964 }
959965 else {
960966 this .logger .debug ("Successfully completed request" );
961967 }
962968 }
963969 }
964- if (this .publishEvents ) {
965- // Whether or not we succeeded, publish an event.
966- long processingTime = System .currentTimeMillis () - startTime ;
967- this .webApplicationContext .publishEvent (
968- new ServletRequestHandledEvent (this ,
969- request .getRequestURI (), request .getRemoteAddr (),
970- request .getMethod (), getServletConfig ().getServletName (),
971- WebUtils .getSessionId (request ), getUsernameForRequest (request ),
972- processingTime , failureCause ));
973- }
970+
971+ publishRequestHandledEvent (request , startTime , failureCause );
974972 }
975973 }
976974
977975 /**
978976 * Build a LocaleContext for the given request, exposing the request's
979977 * primary locale as current locale.
980978 * @param request current HTTP request
981- * @return the corresponding LocaleContext
979+ * @return the corresponding LocaleContext, or {@code null} if none to bind
980+ * @see LocaleContextHolder#setLocaleContext
982981 */
983982 protected LocaleContext buildLocaleContext (HttpServletRequest request ) {
984983 return new SimpleLocaleContext (request .getLocale ());
985984 }
986985
987- private void initContextHolders (HttpServletRequest request ,
988- LocaleContext localeContext , RequestAttributes attributes ) {
986+ /**
987+ * Build ServletRequestAttributes for the given request (potentially also
988+ * holding a reference to the response), taking pre-bound attributes
989+ * (and their type) into consideration.
990+ * @param request current HTTP request
991+ * @param response current HTTP response
992+ * @param previousAttributes pre-bound RequestAttributes instance, if any
993+ * @return the ServletRequestAttributes to bind, or {@code null} to preserve
994+ * the previously bound instance (or not binding any, if none bound before)
995+ * @see RequestContextHolder#setRequestAttributes
996+ */
997+ protected ServletRequestAttributes buildRequestAttributes (
998+ HttpServletRequest request , HttpServletResponse response , RequestAttributes previousAttributes ) {
999+
1000+ if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes ) {
1001+ return new ServletRequestAttributes (request );
1002+ }
1003+ else {
1004+ return null ; // preserve the pre-bound RequestAttributes instance
1005+ }
1006+ }
1007+
1008+ private void initContextHolders (
1009+ HttpServletRequest request , LocaleContext localeContext , RequestAttributes requestAttributes ) {
9891010
990- LocaleContextHolder .setLocaleContext (localeContext , this .threadContextInheritable );
991- if (attributes != null ) {
992- RequestContextHolder .setRequestAttributes (attributes , this .threadContextInheritable );
1011+ if (localeContext != null ) {
1012+ LocaleContextHolder .setLocaleContext (localeContext , this .threadContextInheritable );
1013+ }
1014+ if (requestAttributes != null ) {
1015+ RequestContextHolder .setRequestAttributes (requestAttributes , this .threadContextInheritable );
9931016 }
9941017 if (logger .isTraceEnabled ()) {
9951018 logger .trace ("Bound request context to thread: " + request );
@@ -1006,17 +1029,17 @@ private void resetContextHolders(HttpServletRequest request,
10061029 }
10071030 }
10081031
1009- private CallableProcessingInterceptor getRequestBindingInterceptor ( final HttpServletRequest request ) {
1010- return new CallableProcessingInterceptorAdapter ( ) {
1011- @ Override
1012- public < T > void preProcess ( NativeWebRequest webRequest , Callable < T > task ) {
1013- initContextHolders ( request , buildLocaleContext ( request ), new ServletRequestAttributes ( request ));
1014- }
1015- @ Override
1016- public < T > void postProcess ( NativeWebRequest webRequest , Callable < T > task , Object concurrentResult ) {
1017- resetContextHolders (request , null , null );
1018- }
1019- };
1032+ private void publishRequestHandledEvent ( HttpServletRequest request , long startTime , Throwable failureCause ) {
1033+ if ( this . publishEvents ) {
1034+ // Whether or not we succeeded, publish an event.
1035+ long processingTime = System . currentTimeMillis () - startTime ;
1036+ this . webApplicationContext . publishEvent (
1037+ new ServletRequestHandledEvent ( this ,
1038+ request . getRequestURI (), request . getRemoteAddr (),
1039+ request . getMethod (), getServletConfig (). getServletName (),
1040+ WebUtils . getSessionId (request ), getUsernameForRequest ( request ),
1041+ processingTime , failureCause ));
1042+ }
10201043 }
10211044
10221045 /**
@@ -1032,6 +1055,7 @@ protected String getUsernameForRequest(HttpServletRequest request) {
10321055 return (userPrincipal != null ? userPrincipal .getName () : null );
10331056 }
10341057
1058+
10351059 /**
10361060 * Subclasses must implement this method to do the work of request handling,
10371061 * receiving a centralized callback for GET, POST, PUT and DELETE.
@@ -1049,19 +1073,6 @@ protected abstract void doService(HttpServletRequest request, HttpServletRespons
10491073 throws Exception ;
10501074
10511075
1052- /**
1053- * Close the WebApplicationContext of this servlet.
1054- * @see org.springframework.context.ConfigurableApplicationContext#close()
1055- */
1056- @ Override
1057- public void destroy () {
1058- getServletContext ().log ("Destroying Spring FrameworkServlet '" + getServletName () + "'" );
1059- if (this .webApplicationContext instanceof ConfigurableApplicationContext ) {
1060- ((ConfigurableApplicationContext ) this .webApplicationContext ).close ();
1061- }
1062- }
1063-
1064-
10651076 /**
10661077 * ApplicationListener endpoint that receives events from this servlet's WebApplicationContext
10671078 * only, delegating to {@code onApplicationEvent} on the FrameworkServlet instance.
@@ -1073,4 +1084,28 @@ public void onApplicationEvent(ContextRefreshedEvent event) {
10731084 }
10741085 }
10751086
1087+
1088+ /**
1089+ * CallableProcessingInterceptor implementation that initializes and resets
1090+ * FrameworkServlet's context holders, i.e. LocaleContextHolder and RequestContextHolder.
1091+ */
1092+ private class RequestBindingInterceptor extends CallableProcessingInterceptorAdapter {
1093+
1094+ @ Override
1095+ public <T > void preProcess (NativeWebRequest webRequest , Callable <T > task ) {
1096+ HttpServletRequest request = webRequest .getNativeRequest (HttpServletRequest .class );
1097+ if (request != null ) {
1098+ HttpServletResponse response = webRequest .getNativeRequest (HttpServletResponse .class );
1099+ initContextHolders (request , buildLocaleContext (request ), buildRequestAttributes (request , response , null ));
1100+ }
1101+ }
1102+ @ Override
1103+ public <T > void postProcess (NativeWebRequest webRequest , Callable <T > task , Object concurrentResult ) {
1104+ HttpServletRequest request = webRequest .getNativeRequest (HttpServletRequest .class );
1105+ if (request != null ) {
1106+ resetContextHolders (request , null , null );
1107+ }
1108+ }
1109+ }
1110+
10761111}
0 commit comments