Merge branch '3.2.x'

* 3.2.x: (28 commits)
  Hide 'doc' changes from jdiff reports
  Document @Bean 'lite' mode vs @Configuration
  Final preparations for 3.2.2
  Remove Tiles 3 configuration method
  Polishing
  Extracted buildRequestAttributes template method from FrameworkServlet
  Added "beforeExistingAdvisors" flag to AbstractAdvisingBeanPostProcessor
  Minor refinements along the way of researching static CGLIB callbacks
  Compare Kind references before checking log levels
  Polish Javadoc in RequestAttributes
  Fix copy-n-paste errors in NativeWebRequest
  Fix issue with restoring included attributes
  Add additional test for daylight savings glitch
  Document context hierarchy support in the TCF
  Fix test for daylight savings glitch
  Make the methodParameter field of HandlerMethod final
  Disable AsyncTests in spring-test-mvc
  Reformat the testing chapter
  Document context hierarchy support in the TCF
  Document context hierarchy support in the TCF
  ...
This commit is contained in:
Phillip Webb
2013-03-13 14:01:46 -07:00
267 changed files with 8384 additions and 6246 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -36,6 +35,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -810,13 +810,13 @@ public class DispatcherServlet extends FrameworkServlet {
return context.getAutowireCapableBeanFactory().createBean(clazz);
}
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
@@ -1268,6 +1268,7 @@ public class DispatcherServlet extends FrameworkServlet {
* @param request current HTTP request
* @param attributesSnapshot the snapshot of the request attributes before the include
*/
@SuppressWarnings("unchecked")
private void restoreAttributesAfterInclude(HttpServletRequest request, Map<?,?> attributesSnapshot) {
logger.debug("Restoring snapshot of request attributes after include");
@@ -1282,6 +1283,9 @@ public class DispatcherServlet extends FrameworkServlet {
}
}
// Add attributes that may have been removed
attrsToCheck.addAll((Set<String>) attributesSnapshot.keySet());
// Iterate over the attributes to check, restoring the original value
// or removing the attribute, respectively, if appropriate.
for (String attrName : attrsToCheck) {

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);
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.web.servlet.config;
import java.util.Arrays;
import java.util.Map;
import org.w3c.dom.Element;
@@ -89,7 +90,7 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
}
ManagedList<String> locations = new ManagedList<String>();
locations.addAll(StringUtils.commaDelimitedListToSet(locationAttr));
locations.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(locationAttr)));
RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
resourceHandlerDef.setSource(source);