Extracted buildRequestAttributes template method from FrameworkServlet

Also, RequestBindingInterceptor now obtains HttpServletRequest from locally passed-in request handle and delegates to buildRequestAttributes as well now.

Issue: SPR-10342
This commit is contained in:
Juergen Hoeller 2013-03-13 16:58:29 +01:00
parent 43c1cec79b
commit 1f55b4f2a8
1 changed files with 89 additions and 54 deletions

View File

@ -21,7 +21,6 @@ import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.Callable;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -52,7 +51,6 @@ import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
@ -785,6 +783,18 @@ public abstract class FrameworkServlet extends HttpServletBean {
// For subclasses: do nothing by default.
}
/**
* Close the WebApplicationContext of this servlet.
* @see org.springframework.context.ConfigurableApplicationContext#close()
*/
@Override
public void destroy() {
getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) this.webApplicationContext).close();
}
}
/**
* Override the parent class implementation in order to intercept PATCH
@ -873,7 +883,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
super.doOptions(request, new HttpServletResponseWrapper(response) {
@Override
public void setHeader(String name, String value) {
if("Allow".equals(name)) {
if ("Allow".equals(name)) {
value = (StringUtils.hasLength(value) ? value + ", " : "") + RequestMethod.PATCH.name();
}
super.setHeader(name, value);
@ -915,15 +925,12 @@ public abstract class FrameworkServlet extends HttpServletBean {
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = null;
if (previousAttributes == null || (previousAttributes instanceof ServletRequestAttributes)) {
requestAttributes = new ServletRequestAttributes(request);
}
initContextHolders(request, localeContext, requestAttributes);
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), getRequestBindingInterceptor(request));
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
@ -950,27 +957,18 @@ public abstract class FrameworkServlet extends HttpServletBean {
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
} else {
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
if (logger.isDebugEnabled()) {
logger.debug("Leaving response open for concurrent processing");
}
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
if (this.publishEvents) {
// Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() - startTime;
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause));
}
publishRequestHandledEvent(request, startTime, failureCause);
}
}
@ -978,18 +976,43 @@ public abstract class FrameworkServlet extends HttpServletBean {
* Build a LocaleContext for the given request, exposing the request's
* primary locale as current locale.
* @param request current HTTP request
* @return the corresponding LocaleContext
* @return the corresponding LocaleContext, or {@code null} if none to bind
* @see LocaleContextHolder#setLocaleContext
*/
protected LocaleContext buildLocaleContext(HttpServletRequest request) {
return new SimpleLocaleContext(request.getLocale());
}
private void initContextHolders(HttpServletRequest request,
LocaleContext localeContext, RequestAttributes attributes) {
/**
* Build ServletRequestAttributes for the given request (potentially also
* holding a reference to the response), taking pre-bound attributes
* (and their type) into consideration.
* @param request current HTTP request
* @param response current HTTP response
* @param previousAttributes pre-bound RequestAttributes instance, if any
* @return the ServletRequestAttributes to bind, or {@code null} to preserve
* the previously bound instance (or not binding any, if none bound before)
* @see RequestContextHolder#setRequestAttributes
*/
protected ServletRequestAttributes buildRequestAttributes(
HttpServletRequest request, HttpServletResponse response, RequestAttributes previousAttributes) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
if (attributes != null) {
RequestContextHolder.setRequestAttributes(attributes, this.threadContextInheritable);
if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
return new ServletRequestAttributes(request);
}
else {
return null; // preserve the pre-bound RequestAttributes instance
}
}
private void initContextHolders(
HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
@ -1006,17 +1029,17 @@ public abstract class FrameworkServlet extends HttpServletBean {
}
}
private CallableProcessingInterceptor getRequestBindingInterceptor(final HttpServletRequest request) {
return new CallableProcessingInterceptorAdapter() {
@Override
public <T> void preProcess(NativeWebRequest webRequest, Callable<T> task) {
initContextHolders(request, buildLocaleContext(request), new ServletRequestAttributes(request));
}
@Override
public <T> void postProcess(NativeWebRequest webRequest, Callable<T> task, Object concurrentResult) {
resetContextHolders(request, null, null);
}
};
private void publishRequestHandledEvent(HttpServletRequest request, long startTime, Throwable failureCause) {
if (this.publishEvents) {
// Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() - startTime;
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause));
}
}
/**
@ -1032,6 +1055,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
return (userPrincipal != null ? userPrincipal.getName() : null);
}
/**
* Subclasses must implement this method to do the work of request handling,
* receiving a centralized callback for GET, POST, PUT and DELETE.
@ -1049,19 +1073,6 @@ public abstract class FrameworkServlet extends HttpServletBean {
throws Exception;
/**
* Close the WebApplicationContext of this servlet.
* @see org.springframework.context.ConfigurableApplicationContext#close()
*/
@Override
public void destroy() {
getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) this.webApplicationContext).close();
}
}
/**
* ApplicationListener endpoint that receives events from this servlet's WebApplicationContext
* only, delegating to {@code onApplicationEvent} on the FrameworkServlet instance.
@ -1073,4 +1084,28 @@ public abstract class FrameworkServlet extends HttpServletBean {
}
}
/**
* CallableProcessingInterceptor implementation that initializes and resets
* FrameworkServlet's context holders, i.e. LocaleContextHolder and RequestContextHolder.
*/
private class RequestBindingInterceptor extends CallableProcessingInterceptorAdapter {
@Override
public <T> void preProcess(NativeWebRequest webRequest, Callable<T> task) {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
HttpServletResponse response = webRequest.getNativeRequest(HttpServletResponse.class);
initContextHolders(request, buildLocaleContext(request), buildRequestAttributes(request, response, null));
}
}
@Override
public <T> void postProcess(NativeWebRequest webRequest, Callable<T> task, Object concurrentResult) {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
resetContextHolders(request, null, null);
}
}
}
}