Consistent use of @Nullable across the codebase (even for internals)

Beyond just formally declaring the current behavior, this revision actually enforces non-null behavior in selected signatures now, not tolerating null values anymore when not explicitly documented. It also changes some utility methods with historic null-in/null-out tolerance towards enforced non-null return values, making them a proper citizen in non-null assignments.

Some issues are left as to-do: in particular a thorough revision of spring-test, and a few tests with unclear failures (ignored as "TODO: NULLABLE") to be sorted out in a follow-up commit.

Issue: SPR-15540
This commit is contained in:
Juergen Hoeller
2017-06-07 14:17:48 +02:00
parent ffc3f6d87d
commit f813712f5b
1493 changed files with 10670 additions and 9172 deletions

View File

@@ -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;
@@ -1013,9 +1012,12 @@ public class DispatcherServlet extends FrameworkServlet {
/**
* Do we need view name translation?
*/
private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception {
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
@@ -1024,7 +1026,8 @@ public class DispatcherServlet extends FrameworkServlet {
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
@@ -1077,12 +1080,7 @@ public class DispatcherServlet extends FrameworkServlet {
return ((LocaleContextResolver) this.localeResolver).resolveLocaleContext(request);
}
else {
return new LocaleContext() {
@Override
public Locale getLocale() {
return localeResolver.resolveLocale(request);
}
};
return () -> localeResolver.resolveLocale(request);
}
}
@@ -1237,7 +1235,10 @@ public class DispatcherServlet extends FrameworkServlet {
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
exMv.setViewName(getDefaultViewName(request));
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
@@ -1264,9 +1265,10 @@ public class DispatcherServlet extends FrameworkServlet {
response.setLocale(locale);
View view;
if (mv.isReference()) {
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
@@ -1326,8 +1328,8 @@ public class DispatcherServlet extends FrameworkServlet {
* @see ViewResolver#resolveViewName
*/
@Nullable
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
@@ -1339,7 +1341,7 @@ public class DispatcherServlet extends FrameworkServlet {
}
private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, Exception ex) throws Exception {
@Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, ex);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -61,7 +61,7 @@ public final class FlashMap extends HashMap<String, Object> implements Comparabl
* <p>The path may be absolute (e.g. "/application/resource") or relative to the
* current request (e.g. "../resource").
*/
public void setTargetRequestPath(String path) {
public void setTargetRequestPath(@Nullable String path) {
this.targetRequestPath = path;
}
@@ -77,7 +77,7 @@ public final class FlashMap extends HashMap<String, Object> implements Comparabl
* Provide request parameters identifying the request for this FlashMap.
* @param params a Map with the names and values of expected parameters
*/
public FlashMap addTargetRequestParams(MultiValueMap<String, String> params) {
public FlashMap addTargetRequestParams(@Nullable MultiValueMap<String, String> params) {
if (params != null) {
for (String key : params.keySet()) {
for (String value : params.get(key)) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -21,7 +21,6 @@ import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -288,6 +287,7 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
* Return the name of the ServletContext attribute which should be used to retrieve the
* {@link WebApplicationContext} that this servlet is supposed to use.
*/
@Nullable
public String getContextAttribute() {
return this.contextAttribute;
}
@@ -368,7 +368,7 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
* @see #applyInitializers
*/
@SuppressWarnings("unchecked")
public void setContextInitializers(ApplicationContextInitializer<?>... initializers) {
public void setContextInitializers(@Nullable ApplicationContextInitializer<?>... initializers) {
if (initializers != null) {
for (ApplicationContextInitializer<?> initializer : initializers) {
this.contextInitializers.add((ApplicationContextInitializer<ConfigurableApplicationContext>) initializer);
@@ -629,8 +629,10 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
return wac;
@@ -968,11 +970,7 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
try {
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
@@ -1029,8 +1027,8 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
* @see RequestContextHolder#setRequestAttributes
*/
@Nullable
protected ServletRequestAttributes buildRequestAttributes(
HttpServletRequest request, HttpServletResponse response, @Nullable RequestAttributes previousAttributes) {
protected ServletRequestAttributes buildRequestAttributes(HttpServletRequest request,
@Nullable HttpServletResponse response, @Nullable RequestAttributes previousAttributes) {
if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
return new ServletRequestAttributes(request, response);
@@ -1040,8 +1038,8 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
}
}
private void initContextHolders(
HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {
private void initContextHolders(HttpServletRequest request,
@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
@@ -1064,8 +1062,8 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
}
}
private void publishRequestHandledEvent(
HttpServletRequest request, HttpServletResponse response, long startTime, Throwable failureCause) {
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
long startTime, @Nullable Throwable failureCause) {
if (this.publishEvents) {
// Whether or not we succeeded, publish an event.
@@ -1135,7 +1133,8 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
HttpServletResponse response = webRequest.getNativeRequest(HttpServletResponse.class);
initContextHolders(request, buildLocaleContext(request), buildRequestAttributes(request, response, null));
initContextHolders(request, buildLocaleContext(request),
buildRequestAttributes(request, response, null));
}
}
@Override

View File

@@ -18,7 +18,6 @@ package org.springframework.web.servlet;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -64,7 +63,7 @@ public class HandlerExecutionChain {
* @param interceptors the array of interceptors to apply
* (in the given order) before the handler itself executes
*/
public HandlerExecutionChain(@Nullable Object handler, HandlerInterceptor... interceptors) {
public HandlerExecutionChain(@Nullable Object handler, @Nullable HandlerInterceptor... interceptors) {
if (handler instanceof HandlerExecutionChain) {
HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
this.handler = originalChain.getHandler();
@@ -147,7 +146,9 @@ public class HandlerExecutionChain {
/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -146,7 +146,7 @@ public interface HandlerInterceptor {
* @throws Exception in case of errors
*/
default void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex)
throws Exception {
}

View File

@@ -19,9 +19,7 @@ package org.springframework.web.servlet;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@@ -205,21 +203,10 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
*/
@Override
@Nullable
public final String getServletName() {
public String getServletName() {
return (getServletConfig() != null ? getServletConfig().getServletName() : null);
}
/**
* Overridden method that simply returns {@code null} when no
* ServletConfig set yet.
* @see #getServletConfig()
*/
@Override
@Nullable
public final ServletContext getServletContext() {
return (getServletConfig() != null ? getServletConfig().getServletContext() : null);
}
/**
* PropertyValues implementation created from ServletConfig init parameters.

View File

@@ -145,7 +145,7 @@ public class ModelAndView {
* (to be set just prior to View rendering)
* @since 4.3
*/
public ModelAndView(String viewName, @Nullable Map<String, ?> model, HttpStatus status) {
public ModelAndView(@Nullable String viewName, @Nullable Map<String, ?> model, @Nullable HttpStatus status) {
this.view = viewName;
if (model != null) {
getModelMap().addAllAttributes(model);
@@ -182,7 +182,7 @@ public class ModelAndView {
* DispatcherServlet via a ViewResolver. Will override any
* pre-existing view name or View.
*/
public void setViewName(String viewName) {
public void setViewName(@Nullable String viewName) {
this.view = viewName;
}
@@ -199,7 +199,7 @@ public class ModelAndView {
* Set a View object for this ModelAndView. Will override any
* pre-existing view name or View.
*/
public void setView(View view) {
public void setView(@Nullable View view) {
this.view = view;
}
@@ -304,7 +304,7 @@ public class ModelAndView {
* @see ModelMap#addAllAttributes(Map)
* @see #getModelMap()
*/
public ModelAndView addAllObjects(Map<String, ?> modelMap) {
public ModelAndView addAllObjects(@Nullable Map<String, ?> modelMap) {
getModelMap().addAllAttributes(modelMap);
return this;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2017 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.
@@ -17,7 +17,6 @@
package org.springframework.web.servlet;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -202,16 +201,7 @@ public class ResourceServlet extends HttpServletBean {
try {
doInclude(request, response, resourceUrl);
}
catch (ServletException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to include content of resource [" + resourceUrl + "]", ex);
}
// Try including default URL if appropriate.
if (!includeDefaultUrl(request, response)) {
throw ex;
}
}
catch (IOException ex) {
catch (ServletException | IOException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to include content of resource [" + resourceUrl + "]", ex);
}
@@ -275,16 +265,16 @@ public class ResourceServlet extends HttpServletBean {
}
String[] resourceUrls =
StringUtils.tokenizeToStringArray(resourceUrl, RESOURCE_URL_DELIMITERS);
for (int i = 0; i < resourceUrls.length; i++) {
for (String url : resourceUrls) {
// check whether URL matches allowed resources
if (this.allowedResources != null && !this.pathMatcher.match(this.allowedResources, resourceUrls[i])) {
throw new ServletException("Resource [" + resourceUrls[i] +
if (this.allowedResources != null && !this.pathMatcher.match(this.allowedResources, url)) {
throw new ServletException("Resource [" + url +
"] does not match allowed pattern [" + this.allowedResources + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + resourceUrls[i] + "]");
logger.debug("Including resource [" + url + "]");
}
RequestDispatcher rd = request.getRequestDispatcher(resourceUrls[i]);
RequestDispatcher rd = request.getRequestDispatcher(url);
rd.include(request, response);
}
}
@@ -312,8 +302,8 @@ public class ResourceServlet extends HttpServletBean {
if (resourceUrl != null) {
String[] resourceUrls = StringUtils.tokenizeToStringArray(resourceUrl, RESOURCE_URL_DELIMITERS);
long latestTimestamp = -1;
for (int i = 0; i < resourceUrls.length; i++) {
long timestamp = getFileTimestamp(resourceUrls[i]);
for (String url : resourceUrls) {
long timestamp = getFileTimestamp(url);
if (timestamp > latestTimestamp) {
latestTimestamp = timestamp;
}

View File

@@ -19,6 +19,8 @@ package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
/**
* Interface for web-based theme resolution strategies that allows for
* both theme resolution via the request and theme modification via
@@ -58,10 +60,10 @@ public interface ThemeResolver {
* Set the current theme name to the given one.
* @param request request to be used for theme name modification
* @param response response to be used for theme name modification
* @param themeName the new theme name
* @param themeName the new theme name ({@code null} or empty to reset it)
* @throws UnsupportedOperationException if the ThemeResolver implementation
* does not support dynamic changing of the theme
*/
void setThemeName(HttpServletRequest request, HttpServletResponse response, String themeName);
void setThemeName(HttpServletRequest request, HttpServletResponse response, @Nullable String themeName);
}

View File

@@ -57,6 +57,7 @@ import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConvert
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@@ -340,7 +341,9 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
}
}
private RuntimeBeanReference getConversionService(Element element, Object source, ParserContext parserContext) {
private RuntimeBeanReference getConversionService(
Element element, @Nullable Object source, ParserContext parserContext) {
RuntimeBeanReference conversionServiceRef;
if (element.hasAttribute("conversion-service")) {
conversionServiceRef = new RuntimeBeanReference(element.getAttribute("conversion-service"));
@@ -357,7 +360,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
}
@Nullable
private RuntimeBeanReference getValidator(Element element, Object source, ParserContext parserContext) {
private RuntimeBeanReference getValidator(Element element, @Nullable Object source, ParserContext parserContext) {
if (element.hasAttribute("validator")) {
return new RuntimeBeanReference(element.getAttribute("validator"));
}
@@ -375,7 +378,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
}
}
private RuntimeBeanReference getContentNegotiationManager(Element element, Object source,
private RuntimeBeanReference getContentNegotiationManager(Element element, @Nullable Object source,
ParserContext parserContext) {
RuntimeBeanReference beanRef;
@@ -478,7 +481,9 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
return null;
}
private ManagedList<?> getCallableInterceptors(Element element, Object source, ParserContext parserContext) {
private ManagedList<?> getCallableInterceptors(
Element element, @Nullable Object source, ParserContext parserContext) {
ManagedList<? super Object> interceptors = new ManagedList<>();
Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support");
if (asyncElement != null) {
@@ -487,15 +492,19 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
interceptors.setSource(source);
for (Element converter : DomUtils.getChildElementsByTagName(interceptorsElement, "bean")) {
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(converter);
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef);
interceptors.add(beanDef);
if (beanDef != null) {
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef);
interceptors.add(beanDef);
}
}
}
}
return interceptors;
}
private ManagedList<?> getDeferredResultInterceptors(Element element, Object source, ParserContext parserContext) {
private ManagedList<?> getDeferredResultInterceptors(
Element element, @Nullable Object source, ParserContext parserContext) {
ManagedList<? super Object> interceptors = new ManagedList<>();
Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support");
if (asyncElement != null) {
@@ -504,8 +513,10 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
interceptors.setSource(source);
for (Element converter : DomUtils.getChildElementsByTagName(interceptorsElement, "bean")) {
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(converter);
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef);
interceptors.add(beanDef);
if (beanDef != null) {
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef);
interceptors.add(beanDef);
}
}
}
}
@@ -528,6 +539,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
if (object instanceof BeanDefinitionHolder) {
BeanDefinitionHolder beanDef = (BeanDefinitionHolder) object;
String className = beanDef.getBeanDefinition().getBeanClassName();
Assert.notNull(className, "No resolver class");
Class<?> clazz = ClassUtils.resolveClassName(className, context.getReaderContext().getBeanClassLoader());
if (WebArgumentResolver.class.isAssignableFrom(clazz)) {
RootBeanDefinition adapter = new RootBeanDefinition(ServletWebArgumentResolverAdapter.class);
@@ -546,7 +558,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
return (handlers != null ? extractBeanSubElements(handlers, parserContext) : null);
}
private ManagedList<?> getMessageConverters(Element element, Object source, ParserContext parserContext) {
private ManagedList<?> getMessageConverters(Element element, @Nullable Object source, ParserContext parserContext) {
Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
ManagedList<? super Object> messageConverters = new ManagedList<>();
if (convertersElement != null) {
@@ -613,7 +625,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
return messageConverters;
}
private GenericBeanDefinition createObjectMapperFactoryDefinition(Object source) {
private GenericBeanDefinition createObjectMapperFactoryDefinition(@Nullable Object source) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(Jackson2ObjectMapperFactoryBean.class);
beanDefinition.setSource(source);
@@ -621,7 +633,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
return beanDefinition;
}
private RootBeanDefinition createConverterDefinition(Class<?> converterClass, Object source) {
private RootBeanDefinition createConverterDefinition(Class<?> converterClass, @Nullable Object source) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(converterClass);
beanDefinition.setSource(source);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -80,7 +80,8 @@ public class CorsBeanDefinitionParser implements BeanDefinitionParser {
}
}
MvcNamespaceUtils.registerCorsConfigurations(corsConfigurations, parserContext, parserContext.extractSource(element));
MvcNamespaceUtils.registerCorsConfigurations(
corsConfigurations, parserContext, parserContext.extractSource(element));
return null;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -58,7 +58,7 @@ abstract class MvcNamespaceUtils {
private static final String CORS_CONFIGURATION_BEAN_NAME = "mvcCorsConfigurations";
public static void registerDefaultComponents(ParserContext parserContext, Object source) {
public static void registerDefaultComponents(ParserContext parserContext, @Nullable Object source) {
registerBeanNameUrlHandlerMapping(parserContext, source);
registerHttpRequestHandlerAdapter(parserContext, source);
registerSimpleControllerHandlerAdapter(parserContext, source);
@@ -69,7 +69,9 @@ abstract class MvcNamespaceUtils {
* under that well-known name, unless already registered.
* @return a RuntimeBeanReference to this {@link UrlPathHelper} instance
*/
public static RuntimeBeanReference registerUrlPathHelper(@Nullable RuntimeBeanReference urlPathHelperRef, ParserContext parserContext, Object source) {
public static RuntimeBeanReference registerUrlPathHelper(
@Nullable RuntimeBeanReference urlPathHelperRef, ParserContext parserContext, @Nullable Object source) {
if (urlPathHelperRef != null) {
if (parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME)) {
parserContext.getRegistry().removeAlias(URL_PATH_HELPER_BEAN_NAME);
@@ -92,7 +94,9 @@ abstract class MvcNamespaceUtils {
* under that well-known name, unless already registered.
* @return a RuntimeBeanReference to this {@link PathMatcher} instance
*/
public static RuntimeBeanReference registerPathMatcher(@Nullable RuntimeBeanReference pathMatcherRef, ParserContext parserContext, Object source) {
public static RuntimeBeanReference registerPathMatcher(@Nullable RuntimeBeanReference pathMatcherRef,
ParserContext parserContext, @Nullable Object source) {
if (pathMatcherRef != null) {
if (parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME)) {
parserContext.getRegistry().removeAlias(PATH_MATCHER_BEAN_NAME);
@@ -114,7 +118,7 @@ abstract class MvcNamespaceUtils {
* Registers an {@link HttpRequestHandlerAdapter} under a well-known
* name unless already registered.
*/
private static void registerBeanNameUrlHandlerMapping(ParserContext parserContext, Object source) {
private static void registerBeanNameUrlHandlerMapping(ParserContext parserContext, @Nullable Object source) {
if (!parserContext.getRegistry().containsBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME)){
RootBeanDefinition beanNameMappingDef = new RootBeanDefinition(BeanNameUrlHandlerMapping.class);
beanNameMappingDef.setSource(source);
@@ -131,7 +135,7 @@ abstract class MvcNamespaceUtils {
* Registers an {@link HttpRequestHandlerAdapter} under a well-known
* name unless already registered.
*/
private static void registerHttpRequestHandlerAdapter(ParserContext parserContext, Object source) {
private static void registerHttpRequestHandlerAdapter(ParserContext parserContext, @Nullable Object source) {
if (!parserContext.getRegistry().containsBeanDefinition(HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME)) {
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(HttpRequestHandlerAdapter.class);
handlerAdapterDef.setSource(source);
@@ -145,7 +149,7 @@ abstract class MvcNamespaceUtils {
* Registers a {@link SimpleControllerHandlerAdapter} under a well-known
* name unless already registered.
*/
private static void registerSimpleControllerHandlerAdapter(ParserContext parserContext, Object source) {
private static void registerSimpleControllerHandlerAdapter(ParserContext parserContext, @Nullable Object source) {
if (!parserContext.getRegistry().containsBeanDefinition(SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME)) {
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(SimpleControllerHandlerAdapter.class);
handlerAdapterDef.setSource(source);
@@ -161,7 +165,10 @@ abstract class MvcNamespaceUtils {
* if a non-null CORS configuration is provided.
* @return a RuntimeBeanReference to this {@code Map<String, CorsConfiguration>} instance
*/
public static RuntimeBeanReference registerCorsConfigurations(@Nullable Map<String, CorsConfiguration> corsConfigurations, ParserContext parserContext, Object source) {
public static RuntimeBeanReference registerCorsConfigurations(
@Nullable Map<String, CorsConfiguration> corsConfigurations,
ParserContext parserContext, @Nullable Object source) {
if (!parserContext.getRegistry().containsBeanDefinition(CORS_CONFIGURATION_BEAN_NAME)) {
RootBeanDefinition corsConfigurationsDef = new RootBeanDefinition(LinkedHashMap.class);
corsConfigurationsDef.setSource(source);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -34,10 +34,11 @@ import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.core.Ordered;
import org.springframework.http.CacheControl;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.handler.MappedInterceptor;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
@@ -131,7 +132,7 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
return null;
}
private void registerUrlProvider(ParserContext parserContext, Object source) {
private void registerUrlProvider(ParserContext parserContext, @Nullable Object source) {
if (!parserContext.getRegistry().containsBeanDefinition(RESOURCE_URL_PROVIDER)) {
RootBeanDefinition urlProvider = new RootBeanDefinition(ResourceUrlProvider.class);
urlProvider.setSource(source);
@@ -153,7 +154,7 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
}
}
private String registerResourceHandler(ParserContext parserContext, Element element, Object source) {
private String registerResourceHandler(ParserContext parserContext, Element element, @Nullable Object source) {
String locationAttr = element.getAttribute("location");
if (!StringUtils.hasText(locationAttr)) {
parserContext.getReaderContext().error("The 'location' attribute is required.", parserContext.extractSource(element));
@@ -199,7 +200,7 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
private void parseResourceChain(RootBeanDefinition resourceHandlerDef, ParserContext parserContext,
Element element, Object source) {
Element element, @Nullable Object source) {
String autoRegistration = element.getAttribute("auto-registration");
boolean isAutoRegistration = !(StringUtils.hasText(autoRegistration) && "false".equals(autoRegistration));
@@ -262,7 +263,7 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
}
private void parseResourceCache(ManagedList<? super Object> resourceResolvers,
ManagedList<? super Object> resourceTransformers, Element element, Object source) {
ManagedList<? super Object> resourceTransformers, Element element, @Nullable Object source) {
String resourceCache = element.getAttribute("resource-cache");
if ("true".equals(resourceCache)) {
@@ -302,7 +303,7 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
private void parseResourceResolversTransformers(boolean isAutoRegistration,
ManagedList<? super Object> resourceResolvers, ManagedList<? super Object> resourceTransformers,
ParserContext parserContext, Element element, Object source) {
ParserContext parserContext, Element element, @Nullable Object source) {
Element resolversElement = DomUtils.getChildElementByTagName(element, "resolvers");
if (resolversElement != null) {
@@ -347,7 +348,9 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
}
}
private RootBeanDefinition parseVersionResolver(ParserContext parserContext, Element element, Object source) {
private RootBeanDefinition parseVersionResolver(
ParserContext parserContext, Element element, @Nullable Object source) {
ManagedMap<String, ? super Object> strategyMap = new ManagedMap<>();
strategyMap.setSource(source);
RootBeanDefinition versionResolverDef = new RootBeanDefinition(VersionResourceResolver.class);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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,6 +28,7 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.mvc.ParameterizableViewController;
import org.springframework.web.servlet.view.RedirectView;
@@ -100,11 +101,8 @@ class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
throw new IllegalStateException("Unexpected tag name: " + name);
}
Map<String, BeanDefinition> urlMap;
if (hm.getPropertyValues().contains("urlMap")) {
urlMap = (Map<String, BeanDefinition>) hm.getPropertyValues().getPropertyValue("urlMap").getValue();
}
else {
Map<String, BeanDefinition> urlMap = (Map<String, BeanDefinition>) hm.getPropertyValues().get("urlMap");
if (urlMap == null) {
urlMap = new ManagedMap<>();
hm.getPropertyValues().add("urlMap", urlMap);
}
@@ -113,7 +111,7 @@ class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
return null;
}
private BeanDefinition registerHandlerMapping(ParserContext context, Object source) {
private BeanDefinition registerHandlerMapping(ParserContext context, @Nullable Object source) {
if (context.getRegistry().containsBeanDefinition(HANDLER_MAPPING_BEAN_NAME)) {
return context.getRegistry().getBeanDefinition(HANDLER_MAPPING_BEAN_NAME);
}
@@ -132,7 +130,7 @@ class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
return beanDef;
}
private RootBeanDefinition getRedirectView(Element element, HttpStatus status, Object source) {
private RootBeanDefinition getRedirectView(Element element, @Nullable HttpStatus status, @Nullable Object source) {
RootBeanDefinition redirectView = new RootBeanDefinition(RedirectView.class);
redirectView.setSource(source);
redirectView.getConstructorArgumentValues().addIndexedArgumentValue(0, element.getAttribute("redirect-url"));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.config.annotation;
import java.util.ArrayList;
@@ -22,7 +23,7 @@ import java.util.concurrent.Callable;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.util.Assert;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
@@ -40,27 +41,22 @@ public class AsyncSupportConfigurer {
private Long timeout;
private final List<CallableProcessingInterceptor> callableInterceptors =
new ArrayList<>();
private final List<CallableProcessingInterceptor> callableInterceptors = new ArrayList<>();
private final List<DeferredResultProcessingInterceptor> deferredResultInterceptors =
new ArrayList<>();
private final List<DeferredResultProcessingInterceptor> deferredResultInterceptors = new ArrayList<>();
/**
* Set the default {@link AsyncTaskExecutor} to use when a controller method
* returns a {@link Callable}. Controller methods can override this default on
* a per-request basis by returning a {@link WebAsyncTask}.
*
* <p>By default a {@link SimpleAsyncTaskExecutor} instance is used, and it's
* highly recommended to change that default in production since the simple
* executor does not re-use threads.
*
* <p>As of 5.0 this executor is also used when a controller returns a reactive
* type that does streaming (e.g. "text/event-stream" or
* "application/stream+json") for the blocking writes to the
* {@link javax.servlet.ServletOutputStream}.
*
* @param taskExecutor the task executor instance to use by default
*/
public AsyncSupportConfigurer setTaskExecutor(AsyncTaskExecutor taskExecutor) {
@@ -75,7 +71,6 @@ public class AsyncSupportConfigurer {
* for further processing of the concurrently produced result.
* <p>If this value is not set, the default timeout of the underlying
* implementation is used, e.g. 10 seconds on Tomcat with Servlet 3.
*
* @param timeout the timeout value in milliseconds
*/
public AsyncSupportConfigurer setDefaultTimeout(long timeout) {
@@ -87,11 +82,9 @@ public class AsyncSupportConfigurer {
* Configure lifecycle interceptors with callbacks around concurrent request
* execution that starts when a controller returns a
* {@link java.util.concurrent.Callable}.
*
* @param interceptors the interceptors to register
*/
public AsyncSupportConfigurer registerCallableInterceptors(CallableProcessingInterceptor... interceptors) {
Assert.notNull(interceptors, "Interceptors are required");
this.callableInterceptors.addAll(Arrays.asList(interceptors));
return this;
}
@@ -99,19 +92,20 @@ public class AsyncSupportConfigurer {
/**
* Configure lifecycle interceptors with callbacks around concurrent request
* execution that starts when a controller returns a {@link DeferredResult}.
*
* @param interceptors the interceptors to register
*/
public AsyncSupportConfigurer registerDeferredResultInterceptors(DeferredResultProcessingInterceptor... interceptors) {
Assert.notNull(interceptors, "Interceptors are required");
this.deferredResultInterceptors.addAll(Arrays.asList(interceptors));
return this;
}
@Nullable
protected AsyncTaskExecutor getTaskExecutor() {
return this.taskExecutor;
}
@Nullable
protected Long getTimeout() {
return this.timeout;
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright 2002-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.config.annotation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ObjectUtils;
/**
* A simple configuration condition that checks for the absence of any beans
* of a given type.
*
* @author Rossen Stoyanchev
* @since 4.1
*/
class BeanTypeNotPresentCondition implements ConfigurationCondition {
private static final Log logger =
LogFactory.getLog("org.springframework.web.servlet.config.annotation.ViewResolution");
private final Class<?> beanType;
BeanTypeNotPresentCondition(Class<?> beanType) {
this.beanType = beanType;
}
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.PARSE_CONFIGURATION;
}
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ListableBeanFactory factory = context.getBeanFactory();
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, this.beanType, false, false);
if (ObjectUtils.isEmpty(names)) {
logger.debug("No bean of type [" + this.beanType + "]. Conditional configuration applies.");
return true;
}
else {
logger.debug("Found bean of type [" + this.beanType + "]. Conditional configuration does not apply.");
return false;
}
}
}

View File

@@ -13,16 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.config.annotation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.lang.Nullable;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.accept.ContentNegotiationManagerFactoryBean;
import org.springframework.web.accept.ContentNegotiationStrategy;
@@ -39,34 +40,34 @@ import org.springframework.web.accept.PathExtensionContentNegotiationStrategy;
*
* <table>
* <tr>
* <th>Configurer Property</th>
* <th>Underlying Strategy</th>
* <th>Default Setting</th>
* <th>Configurer Property</th>
* <th>Underlying Strategy</th>
* <th>Default Setting</th>
* </tr>
* <tr>
* <td>{@link #favorPathExtension}</td>
* <td>{@link PathExtensionContentNegotiationStrategy Path Extension strategy}</td>
* <td>On</td>
* <td>{@link #favorPathExtension}</td>
* <td>{@link PathExtensionContentNegotiationStrategy Path Extension strategy}</td>
* <td>On</td>
* </tr>
* <tr>
* <td>{@link #favorParameter}</td>
* <td>{@link ParameterContentNegotiationStrategy Parameter strategy}</td>
* <td>Off</td>
* <td>{@link #favorParameter}</td>
* <td>{@link ParameterContentNegotiationStrategy Parameter strategy}</td>
* <td>Off</td>
* </tr>
* <tr>
* <td>{@link #ignoreAcceptHeader}</td>
* <td>{@link HeaderContentNegotiationStrategy Header strategy}</td>
* <td>On</td>
* <td>{@link #ignoreAcceptHeader}</td>
* <td>{@link HeaderContentNegotiationStrategy Header strategy}</td>
* <td>On</td>
* </tr>
* <tr>
* <td>{@link #defaultContentType}</td>
* <td>{@link FixedContentNegotiationStrategy Fixed content strategy}</td>
* <td>Not set</td>
* <td>{@link #defaultContentType}</td>
* <td>{@link FixedContentNegotiationStrategy Fixed content strategy}</td>
* <td>Not set</td>
* </tr>
* <tr>
* <td>{@link #defaultContentTypeStrategy}</td>
* <td>{@link ContentNegotiationStrategy}</td>
* <td>Not set</td>
* <td>{@link #defaultContentTypeStrategy}</td>
* <td>{@link ContentNegotiationStrategy}</td>
* <td>Not set</td>
* </tr>
* </table>
*
@@ -87,8 +88,7 @@ import org.springframework.web.accept.PathExtensionContentNegotiationStrategy;
*/
public class ContentNegotiationConfigurer {
private final ContentNegotiationManagerFactoryBean factory =
new ContentNegotiationManagerFactoryBean();
private final ContentNegotiationManagerFactoryBean factory = new ContentNegotiationManagerFactoryBean();
private final Map<String, MediaType> mediaTypes = new HashMap<>();
@@ -138,7 +138,7 @@ public class ContentNegotiationConfigurer {
* @see #mediaType(String, MediaType)
* @see #replaceMediaTypes(Map)
*/
public ContentNegotiationConfigurer mediaTypes(Map<String, MediaType> mediaTypes) {
public ContentNegotiationConfigurer mediaTypes(@Nullable Map<String, MediaType> mediaTypes) {
if (mediaTypes != null) {
this.mediaTypes.putAll(mediaTypes);
}
@@ -221,12 +221,9 @@ public class ContentNegotiationConfigurer {
/**
* Set the default content type(s) to use when no content type is requested
* in order of priority.
*
* <p>If destinations are present that do not support any of the given media
* types, consider appending {@link MediaType#ALL} at the end.
*
* <p>By default this is not set.
*
* @see #defaultContentTypeStrategy
*/
public ContentNegotiationConfigurer defaultContentType(MediaType... defaultContentTypes) {
@@ -246,10 +243,10 @@ public class ContentNegotiationConfigurer {
return this;
}
protected ContentNegotiationManager getContentNegotiationManager() throws Exception {
this.factory.addMediaTypes(this.mediaTypes);
this.factory.afterPropertiesSet();
return this.factory.getObject();
return this.factory.build();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -18,7 +18,6 @@ package org.springframework.web.servlet.config.annotation;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import org.springframework.lang.Nullable;
@@ -30,15 +29,18 @@ import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;
/**
* Configures a request handler for serving static resources by forwarding the request to the Servlet container's
* "default" Servlet. This is intended to be used when the Spring MVC {@link DispatcherServlet} is mapped to "/"
* thus overriding the Servlet container's default handling of static resources. Since this handler is configured
* at the lowest precedence, effectively it allows all other handler mappings to handle the request, and if none
* Configures a request handler for serving static resources by forwarding
* the request to the Servlet container's "default" Servlet. This is intended
* to be used when the Spring MVC {@link DispatcherServlet} is mapped to "/"
* thus overriding the Servlet container's default handling of static resources.
*
* <p>Since this handler is configured at the lowest precedence, effectively
* it allows all other handler mappings to handle the request, and if none
* of them do, this handler can forward it to the "default" Servlet.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.1
*
* @see DefaultServletHttpRequestHandler
*/
public class DefaultServletHandlerConfigurer {
@@ -47,19 +49,22 @@ public class DefaultServletHandlerConfigurer {
private DefaultServletHttpRequestHandler handler;
/**
* Create a {@link DefaultServletHandlerConfigurer} instance.
* @param servletContext the ServletContext to use to configure the underlying DefaultServletHttpRequestHandler.
* @param servletContext the ServletContext to use.
*/
public DefaultServletHandlerConfigurer(ServletContext servletContext) {
Assert.notNull(servletContext, "A ServletContext is required to configure default servlet handling");
Assert.notNull(servletContext, "ServletContext is required");
this.servletContext = servletContext;
}
/**
* Enable forwarding to the "default" Servlet. When this method is used the {@link DefaultServletHttpRequestHandler}
* will try to auto-detect the "default" Servlet name. Alternatively, you can specify the name of the default
* Servlet via {@link #enable(String)}.
* Enable forwarding to the "default" Servlet.
* <p>When this method is used the {@link DefaultServletHttpRequestHandler}
* will try to autodetect the "default" Servlet name. Alternatively, you can
* specify the name of the default Servlet via {@link #enable(String)}.
* @see DefaultServletHttpRequestHandler
*/
public void enable() {
@@ -68,28 +73,31 @@ public class DefaultServletHandlerConfigurer {
/**
* Enable forwarding to the "default" Servlet identified by the given name.
* This is useful when the default Servlet cannot be auto-detected, for example when it has been manually configured.
* <p>This is useful when the default Servlet cannot be autodetected,
* for example when it has been manually configured.
* @see DefaultServletHttpRequestHandler
*/
public void enable(@Nullable String defaultServletName) {
handler = new DefaultServletHttpRequestHandler();
handler.setDefaultServletName(defaultServletName);
handler.setServletContext(servletContext);
this.handler = new DefaultServletHttpRequestHandler();
if (defaultServletName != null) {
this.handler.setDefaultServletName(defaultServletName);
}
this.handler.setServletContext(this.servletContext);
}
/**
* Return a handler mapping instance ordered at {@link Integer#MAX_VALUE} containing the
* {@link DefaultServletHttpRequestHandler} instance mapped to {@code "/**"}; or {@code null} if
* default servlet handling was not been enabled.
* {@link DefaultServletHttpRequestHandler} instance mapped to {@code "/**"};
* or {@code null} if default servlet handling was not been enabled.
*/
@Nullable
protected AbstractHandlerMapping getHandlerMapping() {
if (handler == null) {
if (this.handler == null) {
return null;
}
Map<String, HttpRequestHandler> urlMap = new HashMap<>();
urlMap.put("/**", handler);
urlMap.put("/**", this.handler);
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
handlerMapping.setOrder(Integer.MAX_VALUE);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -21,8 +21,8 @@ import java.util.Arrays;
import java.util.List;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.MappedInterceptor;
@@ -88,8 +88,8 @@ public class InterceptorRegistration {
return this.interceptor;
}
String[] include = toArray(this.includePatterns);
String[] exclude = toArray(this.excludePatterns);
String[] include = StringUtils.toStringArray(this.includePatterns);
String[] exclude = StringUtils.toStringArray(this.excludePatterns);
MappedInterceptor mappedInterceptor = new MappedInterceptor(include, exclude, this.interceptor);
if (this.pathMatcher != null) {
@@ -99,8 +99,4 @@ public class InterceptorRegistration {
return mappedInterceptor;
}
private static String[] toArray(List<String> list) {
return (CollectionUtils.isEmpty(list) ? null : list.toArray(new String[list.size()]));
}
}

View File

@@ -28,13 +28,13 @@ import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapt
*
* @author Rossen Stoyanchev
* @author Keith Donald
*
* @since 3.1
*/
public class InterceptorRegistry {
private final List<InterceptorRegistration> registrations = new ArrayList<>();
/**
* Adds the provided {@link HandlerInterceptor}.
* @param interceptor the interceptor to add
@@ -43,7 +43,7 @@ public class InterceptorRegistry {
*/
public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
InterceptorRegistration registration = new InterceptorRegistration(interceptor);
registrations.add(registration);
this.registrations.add(registration);
return registration;
}
@@ -56,16 +56,16 @@ public class InterceptorRegistry {
public InterceptorRegistration addWebRequestInterceptor(WebRequestInterceptor interceptor) {
WebRequestHandlerInterceptorAdapter adapted = new WebRequestHandlerInterceptorAdapter(interceptor);
InterceptorRegistration registration = new InterceptorRegistration(adapted);
registrations.add(registration);
this.registrations.add(registration);
return registration;
}
/**
* Returns all registered interceptors.
* Return all registered interceptors.
*/
protected List<Object> getInterceptors() {
List<Object> interceptors = new ArrayList<>();
for (InterceptorRegistration registration : registrations) {
List<Object> interceptors = new ArrayList<>(this.registrations.size());
for (InterceptorRegistration registration : this.registrations) {
interceptors.add(registration.getInterceptor());
}
return interceptors ;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.annotation;
import org.springframework.lang.Nullable;
import org.springframework.util.PathMatcher;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.ParsingPathMatcher;
@@ -107,26 +108,30 @@ public class PathMatchConfigurer {
return this;
}
@Nullable
public Boolean isUseSuffixPatternMatch() {
return this.suffixPatternMatch;
}
@Nullable
public Boolean isUseTrailingSlashMatch() {
return this.trailingSlashMatch;
}
@Nullable
public Boolean isUseRegisteredSuffixPatternMatch() {
return this.registeredSuffixPatternMatch;
}
@Nullable
public UrlPathHelper getUrlPathHelper() {
return this.urlPathHelper;
}
@Nullable
public PathMatcher getPathMatcher() {
if(this.pathMatcher != null
&& this.pathMatcher.getClass().isAssignableFrom(ParsingPathMatcher.class)
&& (this.trailingSlashMatch || this.suffixPatternMatch)) {
if (this.pathMatcher instanceof ParsingPathMatcher && (this.trailingSlashMatch || this.suffixPatternMatch)) {
throw new IllegalStateException("When using a ParsingPathMatcher, useTrailingSlashMatch" +
" and useSuffixPatternMatch should be set to 'false'.");
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -21,6 +21,7 @@ import java.util.List;
import org.springframework.cache.Cache;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.servlet.resource.CachingResourceResolver;
@@ -60,10 +61,10 @@ public class ResourceChainRegistration {
public ResourceChainRegistration(boolean cacheResources) {
this(cacheResources, cacheResources ? new ConcurrentMapCache(DEFAULT_CACHE_NAME) : null);
this(cacheResources, (cacheResources ? new ConcurrentMapCache(DEFAULT_CACHE_NAME) : null));
}
public ResourceChainRegistration(boolean cacheResources, Cache cache) {
public ResourceChainRegistration(boolean cacheResources, @Nullable Cache cache) {
Assert.isTrue(!cacheResources || cache != null, "'cache' is required when cacheResources=true");
if (cacheResources) {
this.resolvers.add(new CachingResourceResolver(cache));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -21,7 +21,6 @@ import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.BeanInitializationException;
@@ -81,7 +80,7 @@ public class ResourceHandlerRegistry {
* @since 4.3
*/
public ResourceHandlerRegistry(ApplicationContext applicationContext, ServletContext servletContext,
ContentNegotiationManager contentNegotiationManager) {
@Nullable ContentNegotiationManager contentNegotiationManager) {
Assert.notNull(applicationContext, "ApplicationContext is required");
this.applicationContext = applicationContext;

View File

@@ -22,7 +22,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
@@ -258,32 +257,37 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
*/
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = createRequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
handlerMapping.setCorsConfigurations(getCorsConfigurations());
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors());
mapping.setContentNegotiationManager(mvcContentNegotiationManager());
mapping.setCorsConfigurations(getCorsConfigurations());
PathMatchConfigurer configurer = getPathMatchConfigurer();
if (configurer.isUseSuffixPatternMatch() != null) {
handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
if (configurer.isUseTrailingSlashMatch() != null) {
handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
}
UrlPathHelper pathHelper = configurer.getUrlPathHelper();
if (pathHelper != null) {
handlerMapping.setUrlPathHelper(pathHelper);
}
PathMatcher pathMatcher = configurer.getPathMatcher();
if (pathMatcher != null) {
handlerMapping.setPathMatcher(pathMatcher);
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
return handlerMapping;
UrlPathHelper pathHelper = configurer.getUrlPathHelper();
if (pathHelper != null) {
mapping.setUrlPathHelper(pathHelper);
}
PathMatcher pathMatcher = configurer.getPathMatcher();
if (pathMatcher != null) {
mapping.setPathMatcher(pathMatcher);
}
return mapping;
}
/**
@@ -579,7 +583,10 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
initializer.setConversionService(mvcConversionService());
initializer.setValidator(mvcValidator());
initializer.setMessageCodesResolver(getMessageCodesResolver());
MessageCodesResolver messageCodesResolver = getMessageCodesResolver();
if (messageCodesResolver != null) {
initializer.setMessageCodesResolver(messageCodesResolver);
}
return initializer;
}

View File

@@ -55,14 +55,12 @@ public interface WebMvcConfigurer {
* <li>ViewControllerMappings</li>
* <li>ResourcesMappings</li>
* </ul>
*
* <p>Note that if a {@link org.springframework.web.util.pattern.ParsingPathMatcher}
* is configured here,
* the {@link PathMatchConfigurer#setUseTrailingSlashMatch(Boolean)} and
* {@link PathMatchConfigurer#setUseSuffixPatternMatch(Boolean)} options must be set
* to {@literal false}as they can lead to illegal patterns,
* see {@link org.springframework.web.util.pattern.ParsingPathMatcher}.
*
* @since 4.0.3
*/
default void configurePathMatch(PathMatchConfigurer configurer) {

View File

@@ -18,6 +18,7 @@ package org.springframework.web.servlet.handler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
@@ -68,12 +69,13 @@ public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHand
* @see #determineUrlsForHandler(String)
*/
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
logger.debug("Looking for URL mappings in application context: " + applicationContext);
}
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -21,7 +21,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -194,12 +193,12 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
* <p>Supported interceptor types are HandlerInterceptor, WebRequestInterceptor, and MappedInterceptor.
* Mapped interceptors apply only to request URLs that match its path patterns.
* Mapped interceptor beans are also detected by type during initialization.
* @param interceptors array of handler interceptors, or {@code null} if none
* @param interceptors array of handler interceptors
* @see #adaptInterceptor
* @see org.springframework.web.servlet.HandlerInterceptor
* @see org.springframework.web.context.request.WebRequestInterceptor
*/
public void setInterceptors(@Nullable Object... interceptors) {
public void setInterceptors(Object... interceptors) {
this.interceptors.addAll(Arrays.asList(interceptors));
}
@@ -273,7 +272,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(), MappedInterceptor.class, true, false).values());
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
/**
@@ -364,7 +363,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
handler = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
@@ -439,16 +438,17 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
* Retrieve the CORS configuration for the given handler.
* @param handler the handler to check (never {@code null}).
* @param request the current request.
* @return the CORS configuration for the handler or {@code null}.
* @return the CORS configuration for the handler, or {@code null} if none
* @since 4.2
*/
@Nullable
protected CorsConfiguration getCorsConfiguration(Object handler, HttpServletRequest request) {
Object resolvedHandler = handler;
if (handler instanceof HandlerExecutionChain) {
handler = ((HandlerExecutionChain) handler).getHandler();
resolvedHandler = ((HandlerExecutionChain) handler).getHandler();
}
if (handler instanceof CorsConfigurationSource) {
return ((CorsConfigurationSource) handler).getCorsConfiguration(request);
if (resolvedHandler instanceof CorsConfigurationSource) {
return ((CorsConfigurationSource) resolvedHandler).getCorsConfiguration(request);
}
return null;
}
@@ -462,7 +462,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
* HandlerInterceptor that makes CORS-related checks and adds CORS headers.
* @param request the current request
* @param chain the handler chain
* @param config the applicable CORS configuration, possibly {@code null}
* @param config the applicable CORS configuration (possibly {@code null})
* @since 4.2
*/
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
@@ -483,7 +483,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
private final CorsConfiguration config;
public PreFlightHandler(CorsConfiguration config) {
public PreFlightHandler(@Nullable CorsConfiguration config) {
this.config = config;
}
@@ -503,7 +503,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
private final CorsConfiguration config;
public CorsInterceptor(CorsConfiguration config) {
public CorsInterceptor(@Nullable CorsConfiguration config) {
this.config = config;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -41,7 +41,7 @@ public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHan
@Override
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
if (handler == null) {
return super.shouldApplyTo(request, handler);
return super.shouldApplyTo(request, null);
}
else if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;

View File

@@ -28,7 +28,6 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -199,14 +198,14 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().getType(beanName);
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
@@ -228,13 +227,12 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
*/
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);
obtainApplicationContext().getType((String) handler) : handler.getClass());
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
if (handlerType != null) {
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
@@ -242,16 +240,15 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
}
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
for (Map.Entry<Method, T> entry : methods.entrySet()) {
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
registerHandlerMethod(handler, invocableMethod, mapping);
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
for (Map.Entry<Method, T> entry : methods.entrySet()) {
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
registerHandlerMethod(handler, invocableMethod, mapping);
}
}
}
@@ -279,7 +276,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
if (handler instanceof String) {
String beanName = (String) handler;
handlerMethod = new HandlerMethod(beanName,
getApplicationContext().getAutowireCapableBeanFactory(), method);
obtainApplicationContext().getAutowireCapableBeanFactory(), method);
}
else {
handlerMethod = new HandlerMethod(handler, method);
@@ -507,6 +504,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
* Return matches for the given URL path. Not thread-safe.
* @see #acquireReadLock()
*/
@Nullable
public List<T> getMappingsByUrl(String urlPath) {
return this.urlLookup.get(urlPath);
}
@@ -687,7 +685,9 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
private final String mappingName;
public MappingRegistration(T mapping, HandlerMethod handlerMethod, List<String> directUrls, String mappingName) {
public MappingRegistration(T mapping, HandlerMethod handlerMethod,
@Nullable List<String> directUrls, @Nullable String mappingName) {
Assert.notNull(mapping, "Mapping must not be null");
Assert.notNull(handlerMethod, "HandlerMethod must not be null");
this.mapping = mapping;
@@ -708,6 +708,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
return this.directUrls;
}
@Nullable
public String getMappingName() {
return this.mappingName;
}

View File

@@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@@ -134,7 +135,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
@@ -170,7 +171,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
@@ -212,7 +213,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
@@ -335,8 +336,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
resolvedHandler = getApplicationContext().getBean(handlerName);
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}

View File

@@ -59,7 +59,7 @@ public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMappin
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = getApplicationContext().getAliases(beanName);
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.
@@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.request.WebRequestInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
@@ -68,7 +69,9 @@ public final class MappedInterceptor implements HandlerInterceptor {
* @param excludePatterns the path patterns to exclude
* @param interceptor the HandlerInterceptor instance to map to the given patterns
*/
public MappedInterceptor(@Nullable String[] includePatterns, String[] excludePatterns, HandlerInterceptor interceptor) {
public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns,
HandlerInterceptor interceptor) {
this.includePatterns = includePatterns;
this.excludePatterns = excludePatterns;
this.interceptor = interceptor;
@@ -89,7 +92,9 @@ public final class MappedInterceptor implements HandlerInterceptor {
* @param includePatterns the path patterns to map with a {@code null} value matching to all paths
* @param interceptor the WebRequestInterceptor instance to map to the given patterns
*/
public MappedInterceptor(@Nullable String[] includePatterns, String[] excludePatterns, WebRequestInterceptor interceptor) {
public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns,
WebRequestInterceptor interceptor) {
this(includePatterns, excludePatterns, new WebRequestHandlerInterceptorAdapter(interceptor));
}
@@ -100,8 +105,6 @@ public final class MappedInterceptor implements HandlerInterceptor {
* method. This is an advanced property that is only required when using custom
* PathMatcher implementations that support mapping metadata other than the
* Ant-style path patterns supported by default.
*
* @param pathMatcher the path matcher to use
*/
public void setPathMatcher(PathMatcher pathMatcher) {
this.pathMatcher = pathMatcher;
@@ -135,12 +138,18 @@ public final class MappedInterceptor implements HandlerInterceptor {
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView)
throws Exception {
this.interceptor.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex)
throws Exception {
this.interceptor.afterCompletion(request, response, handler, ex);
}
@@ -158,7 +167,7 @@ public final class MappedInterceptor implements HandlerInterceptor {
}
}
}
if (this.includePatterns == null) {
if (ObjectUtils.isEmpty(this.includePatterns)) {
return true;
}
else {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -26,6 +26,7 @@ import javax.servlet.ServletException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;
@@ -157,6 +158,7 @@ public class SimpleServletPostProcessor implements
}
@Override
@Nullable
public String getInitParameter(String paramName) {
return null;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2017 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.
@@ -60,7 +60,8 @@ public abstract class AbstractLocaleContextResolver extends AbstractLocaleResolv
@Override
public Locale resolveLocale(HttpServletRequest request) {
return resolveLocaleContext(request).getLocale();
Locale locale = resolveLocaleContext(request).getLocale();
return (locale != null ? locale : request.getLocale());
}
@Override

View File

@@ -53,7 +53,7 @@ public class AcceptHeaderLocaleResolver implements LocaleResolver {
* @param locales the supported locales
* @since 4.3
*/
public void setSupportedLocales(List<Locale> locales) {
public void setSupportedLocales(@Nullable List<Locale> locales) {
this.supportedLocales.clear();
if (locales != null) {
this.supportedLocales.addAll(locales);

View File

@@ -18,7 +18,6 @@ package org.springframework.web.servlet.i18n;
import java.util.Locale;
import java.util.TimeZone;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -260,6 +259,7 @@ public class CookieLocaleResolver extends CookieGenerator implements LocaleConte
* @return the corresponding {@code Locale} instance
* @since 4.3
*/
@Nullable
protected Locale parseLocaleValue(String locale) {
return (isLanguageTagCompliant() ? Locale.forLanguageTag(locale) : StringUtils.parseLocaleString(locale));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
@@ -182,6 +183,7 @@ public class LocaleChangeInterceptor extends HandlerInterceptorAdapter {
* @return the corresponding {@code Locale} instance
* @since 4.3
*/
@Nullable
protected Locale parseLocaleValue(String locale) {
return (isLanguageTagCompliant() ? Locale.forLanguageTag(locale) : StringUtils.parseLocaleString(locale));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.
@@ -17,11 +17,13 @@
package org.springframework.web.servlet.mvc;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.util.Assert;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.WebUtils;
@@ -117,10 +119,13 @@ public class ServletForwardingController extends AbstractController implements B
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
RequestDispatcher rd = getServletContext().getNamedDispatcher(this.servletName);
ServletContext servletContext = getServletContext();
Assert.state(servletContext != null, "No ServletContext");
RequestDispatcher rd = servletContext.getNamedDispatcher(this.servletName);
if (rd == null) {
throw new ServletException("No servlet with name '" + this.servletName + "' defined in web.xml");
}
// If already included, include again, else forward.
if (useInclude(request, response)) {
rd.include(request, response);
@@ -136,6 +141,7 @@ public class ServletForwardingController extends AbstractController implements B
"] in ServletForwardingController '" + this.beanName + "'");
}
}
return null;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.ModelAndView;
@@ -183,6 +184,7 @@ public class ServletWrappingController extends AbstractController
}
@Override
@Nullable
public ServletContext getServletContext() {
return ServletWrappingController.this.getServletContext();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -20,6 +20,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerMapping;
@@ -60,7 +61,7 @@ public class UrlFilenameViewController extends AbstractUrlViewController {
* Set the prefix to prepend to the request URL filename
* to build a view name.
*/
public void setPrefix(String prefix) {
public void setPrefix(@Nullable String prefix) {
this.prefix = (prefix != null ? prefix : "");
}
@@ -75,7 +76,7 @@ public class UrlFilenameViewController extends AbstractUrlViewController {
* Set the suffix to append to the request URL filename
* to build a view name.
*/
public void setSuffix(String suffix) {
public void setSuffix(@Nullable String suffix) {
this.suffix = (suffix != null ? suffix : "");
}

View File

@@ -18,6 +18,7 @@ package org.springframework.web.servlet.mvc.annotation;
import java.lang.reflect.Method;
import org.springframework.lang.Nullable;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.servlet.ModelAndView;
@@ -52,7 +53,7 @@ public interface ModelAndViewResolver {
ModelAndView UNRESOLVED = new ModelAndView();
ModelAndView resolveModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue,
ExtendedModelMap implicitModel, NativeWebRequest webRequest);
ModelAndView resolveModelAndView(Method handlerMethod, Class<?> handlerType,
@Nullable Object returnValue, ExtendedModelMap implicitModel, NativeWebRequest webRequest);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -20,6 +20,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
@@ -73,7 +74,7 @@ abstract class AbstractMediaTypeExpression implements MediaTypeExpression, Compa
}
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -35,6 +35,7 @@ abstract class AbstractNameValueExpression<T> implements NameValueExpression<T>
protected final boolean isNegated;
AbstractNameValueExpression(String expression) {
int separator = expression.indexOf('=');
if (separator == -1) {
@@ -49,6 +50,7 @@ abstract class AbstractNameValueExpression<T> implements NameValueExpression<T>
}
}
@Override
public String getName() {
return this.name;
@@ -64,10 +66,6 @@ abstract class AbstractNameValueExpression<T> implements NameValueExpression<T>
return this.isNegated;
}
protected abstract boolean isCaseSensitiveName();
protected abstract T parseValue(String valueExpression);
public final boolean match(HttpServletRequest request) {
boolean isMatch;
if (this.value != null) {
@@ -76,23 +74,29 @@ abstract class AbstractNameValueExpression<T> implements NameValueExpression<T>
else {
isMatch = matchName(request);
}
return isNegated ? !isMatch : isMatch;
return (this.isNegated ? !isMatch : isMatch);
}
protected abstract boolean isCaseSensitiveName();
protected abstract T parseValue(String valueExpression);
protected abstract boolean matchName(HttpServletRequest request);
protected abstract boolean matchValue(HttpServletRequest request);
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj instanceof AbstractNameValueExpression) {
if (obj instanceof AbstractNameValueExpression) {
AbstractNameValueExpression<?> other = (AbstractNameValueExpression<?>) obj;
String thisName = isCaseSensitiveName() ? this.name : this.name.toLowerCase();
String otherName = isCaseSensitiveName() ? other.name : other.name.toLowerCase();
return ((thisName.equalsIgnoreCase(otherName)) &&
String thisName = (isCaseSensitiveName() ? this.name : this.name.toLowerCase());
String otherName = (isCaseSensitiveName() ? other.name : other.name.toLowerCase());
return (thisName.equalsIgnoreCase(otherName) &&
(this.value != null ? this.value.equals(other.value) : other.value == null) &&
this.isNegated == other.isNegated);
}
@@ -101,28 +105,28 @@ abstract class AbstractNameValueExpression<T> implements NameValueExpression<T>
@Override
public int hashCode() {
int result = isCaseSensitiveName() ? name.hashCode() : name.toLowerCase().hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
result = 31 * result + (isNegated ? 1 : 0);
int result = (isCaseSensitiveName() ? this.name.hashCode() : this.name.toLowerCase().hashCode());
result = 31 * result + (this.value != null ? this.value.hashCode() : 0);
result = 31 * result + (this.isNegated ? 1 : 0);
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (value != null) {
builder.append(name);
if (isNegated) {
if (this.value != null) {
builder.append(this.name);
if (this.isNegated) {
builder.append('!');
}
builder.append('=');
builder.append(value);
builder.append(this.value);
}
else {
if (isNegated) {
if (this.isNegated) {
builder.append('!');
}
builder.append(name);
builder.append(this.name);
}
return builder.toString();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -19,6 +19,8 @@ package org.springframework.web.servlet.mvc.condition;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.lang.Nullable;
/**
* A base class for {@link RequestCondition} types providing implementations of
* {@link #equals(Object)}, {@link #hashCode()}, and {@link #toString()}.
@@ -28,8 +30,32 @@ import java.util.Iterator;
*/
public abstract class AbstractRequestCondition<T extends AbstractRequestCondition<T>> implements RequestCondition<T> {
/**
* Indicates whether this condition is empty, i.e. whether or not it
* contains any discrete items.
* @return {@code true} if empty; {@code false} otherwise
*/
public boolean isEmpty() {
return getContent().isEmpty();
}
/**
* Return the discrete items a request condition is composed of.
* <p>For example URL patterns, HTTP request methods, param expressions, etc.
* @return a collection of objects, never {@code null}
*/
protected abstract Collection<?> getContent();
/**
* The notation to use when printing discrete items of content.
* <p>For example {@code " || "} for URL patterns or {@code " && "}
* for param expressions.
*/
protected abstract String getToStringInfix();
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -59,28 +85,4 @@ public abstract class AbstractRequestCondition<T extends AbstractRequestConditio
return builder.toString();
}
/**
* Indicates whether this condition is empty, i.e. whether or not it
* contains any discrete items.
* @return {@code true} if empty; {@code false} otherwise
*/
public boolean isEmpty() {
return getContent().isEmpty();
}
/**
* Return the discrete items a request condition is composed of.
* <p>For example URL patterns, HTTP request methods, param expressions, etc.
* @return a collection of objects, never {@code null}
*/
protected abstract Collection<?> getContent();
/**
* The notation to use when printing discrete items of content.
* <p>For example {@code " || "} for URL patterns or {@code " && "}
* for param expressions.
*/
protected abstract String getToStringInfix();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -51,7 +51,7 @@ public class CompositeRequestCondition extends AbstractRequestCondition<Composit
* same number of conditions so they may be compared and combined.
* It is acceptable to provide {@code null} conditions.
*/
public CompositeRequestCondition(@Nullable RequestCondition<?>... requestConditions) {
public CompositeRequestCondition(RequestCondition<?>... requestConditions) {
this.requestConditions = wrap(requestConditions);
}

View File

@@ -28,6 +28,7 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.cors.CorsUtils;
@@ -71,7 +72,7 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
* @param consumes as described in {@link RequestMapping#consumes()}
* @param headers as described in {@link RequestMapping#headers()}
*/
public ConsumesRequestCondition(String[] consumes, String[] headers) {
public ConsumesRequestCondition(String[] consumes, @Nullable String[] headers) {
this(parseExpressions(consumes, headers));
}
@@ -84,7 +85,7 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
}
private static Set<ConsumeMediaTypeExpression> parseExpressions(String[] consumes, String[] headers) {
private static Set<ConsumeMediaTypeExpression> parseExpressions(String[] consumes, @Nullable String[] headers) {
Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>();
if (headers != null) {
for (String header : headers) {
@@ -96,10 +97,8 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
}
}
}
if (consumes != null) {
for (String consume : consumes) {
result.add(new ConsumeMediaTypeExpression(consume));
}
for (String consume : consumes) {
result.add(new ConsumeMediaTypeExpression(consume));
}
return result;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -63,14 +63,12 @@ public final class HeadersRequestCondition extends AbstractRequestCondition<Head
private static Collection<HeaderExpression> parseExpressions(String... headers) {
Set<HeaderExpression> expressions = new LinkedHashSet<>();
if (headers != null) {
for (String header : headers) {
HeaderExpression expr = new HeaderExpression(header);
if ("Accept".equalsIgnoreCase(expr.name) || "Content-Type".equalsIgnoreCase(expr.name)) {
continue;
}
expressions.add(expr);
for (String header : headers) {
HeaderExpression expr = new HeaderExpression(header);
if ("Accept".equalsIgnoreCase(expr.name) || "Content-Type".equalsIgnoreCase(expr.name)) {
continue;
}
expressions.add(expr);
}
return expressions;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -54,10 +54,8 @@ public final class ParamsRequestCondition extends AbstractRequestCondition<Param
private static Collection<ParamExpression> parseExpressions(String... params) {
Set<ParamExpression> expressions = new LinkedHashSet<>();
if (params != null) {
for (String param : params) {
expressions.add(new ParamExpression(param));
}
for (String param : params) {
expressions.add(new ParamExpression(param));
}
return expressions;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -25,9 +25,9 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.lang.Nullable;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
@@ -61,7 +61,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
* @param patterns 0 or more URL patterns; if 0 the condition will match to every request.
*/
public PatternsRequestCondition(String... patterns) {
this(asList(patterns), null, null, true, true, null);
this(Arrays.asList(patterns), null, null, true, true, null);
}
/**
@@ -73,10 +73,10 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
* @param useSuffixPatternMatch whether to enable matching by suffix (".*")
* @param useTrailingSlashMatch whether to match irrespective of a trailing slash
*/
public PatternsRequestCondition(String[] patterns, UrlPathHelper urlPathHelper, PathMatcher pathMatcher,
boolean useSuffixPatternMatch, boolean useTrailingSlashMatch) {
public PatternsRequestCondition(String[] patterns, @Nullable UrlPathHelper urlPathHelper,
@Nullable PathMatcher pathMatcher, boolean useSuffixPatternMatch, boolean useTrailingSlashMatch) {
this(asList(patterns), urlPathHelper, pathMatcher, useSuffixPatternMatch, useTrailingSlashMatch, null);
this(Arrays.asList(patterns), urlPathHelper, pathMatcher, useSuffixPatternMatch, useTrailingSlashMatch, null);
}
/**
@@ -89,25 +89,27 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
* @param useTrailingSlashMatch whether to match irrespective of a trailing slash
* @param fileExtensions a list of file extensions to consider for path matching
*/
public PatternsRequestCondition(String[] patterns, UrlPathHelper urlPathHelper,
PathMatcher pathMatcher, boolean useSuffixPatternMatch, boolean useTrailingSlashMatch,
List<String> fileExtensions) {
public PatternsRequestCondition(String[] patterns, @Nullable UrlPathHelper urlPathHelper,
@Nullable PathMatcher pathMatcher, boolean useSuffixPatternMatch,
boolean useTrailingSlashMatch, @Nullable List<String> fileExtensions) {
this(asList(patterns), urlPathHelper, pathMatcher, useSuffixPatternMatch, useTrailingSlashMatch, fileExtensions);
this(Arrays.asList(patterns), urlPathHelper, pathMatcher, useSuffixPatternMatch,
useTrailingSlashMatch, fileExtensions);
}
/**
* Private constructor accepting a collection of patterns.
*/
private PatternsRequestCondition(Collection<String> patterns, UrlPathHelper urlPathHelper,
PathMatcher pathMatcher, boolean useSuffixPatternMatch, boolean useTrailingSlashMatch,
List<String> fileExtensions) {
private PatternsRequestCondition(Collection<String> patterns, @Nullable UrlPathHelper urlPathHelper,
@Nullable PathMatcher pathMatcher, boolean useSuffixPatternMatch,
boolean useTrailingSlashMatch, @Nullable List<String> fileExtensions) {
this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
this.pathHelper = (urlPathHelper != null ? urlPathHelper : new UrlPathHelper());
this.pathMatcher = (pathMatcher != null ? pathMatcher : new AntPathMatcher());
this.useSuffixPatternMatch = useSuffixPatternMatch;
this.useTrailingSlashMatch = useTrailingSlashMatch;
if (fileExtensions != null) {
for (String fileExtension : fileExtensions) {
if (fileExtension.charAt(0) != '.') {
@@ -119,14 +121,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
}
private static List<String> asList(String... patterns) {
return (patterns != null ? Arrays.asList(patterns) : Collections.emptyList());
}
private static Set<String> prependLeadingSlash(Collection<String> patterns) {
if (patterns == null) {
return Collections.emptySet();
}
Set<String> result = new LinkedHashSet<>(patterns.size());
for (String pattern : patterns) {
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -26,6 +26,7 @@ import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.accept.ContentNegotiationManager;
@@ -66,7 +67,7 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
* @param produces expressions with syntax defined by {@link RequestMapping#produces()}
*/
public ProducesRequestCondition(String... produces) {
this(produces, (String[]) null);
this(produces, null, null);
}
/**
@@ -77,7 +78,7 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
* @param produces expressions with syntax defined by {@link RequestMapping#produces()}
* @param headers expressions with syntax defined by {@link RequestMapping#headers()}
*/
public ProducesRequestCondition(String[] produces, String[] headers) {
public ProducesRequestCondition(String[] produces, @Nullable String[] headers) {
this(produces, headers, null);
}
@@ -88,7 +89,9 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
* @param headers expressions with syntax defined by {@link RequestMapping#headers()}
* @param manager used to determine requested media types
*/
public ProducesRequestCondition(String[] produces, String[] headers, ContentNegotiationManager manager) {
public ProducesRequestCondition(String[] produces, @Nullable String[] headers,
@Nullable ContentNegotiationManager manager) {
this.expressions = new ArrayList<>(parseExpressions(produces, headers));
Collections.sort(this.expressions);
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
@@ -97,14 +100,16 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
/**
* Private constructor with already parsed media type expressions.
*/
private ProducesRequestCondition(Collection<ProduceMediaTypeExpression> expressions, ContentNegotiationManager manager) {
private ProducesRequestCondition(Collection<ProduceMediaTypeExpression> expressions,
@Nullable ContentNegotiationManager manager) {
this.expressions = new ArrayList<>(expressions);
Collections.sort(this.expressions);
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
}
private Set<ProduceMediaTypeExpression> parseExpressions(String[] produces, String[] headers) {
private Set<ProduceMediaTypeExpression> parseExpressions(String[] produces, @Nullable String[] headers) {
Set<ProduceMediaTypeExpression> result = new LinkedHashSet<>();
if (headers != null) {
for (String header : headers) {
@@ -116,10 +121,8 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
}
}
}
if (produces != null) {
for (String produce : produces) {
result.add(new ProduceMediaTypeExpression(produce));
}
for (String produce : produces) {
result.add(new ProduceMediaTypeExpression(produce));
}
return result;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -20,13 +20,13 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.cors.CorsUtils;
@@ -43,7 +43,6 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi
private static final RequestMethodsRequestCondition GET_CONDITION =
new RequestMethodsRequestCondition(RequestMethod.GET);
private final Set<RequestMethod> methods;
@@ -53,7 +52,7 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi
* if, 0 the condition will match to every request
*/
public RequestMethodsRequestCondition(RequestMethod... requestMethods) {
this(asList(requestMethods));
this(Arrays.asList(requestMethods));
}
private RequestMethodsRequestCondition(Collection<RequestMethod> requestMethods) {
@@ -61,11 +60,6 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi
}
private static List<RequestMethod> asList(RequestMethod... requestMethods) {
return (requestMethods != null ? Arrays.asList(requestMethods) : Collections.emptyList());
}
/**
* Returns all {@link RequestMethod}s contained in this condition.
*/
@@ -126,6 +120,7 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi
* Hence empty conditions is a match, otherwise try to match to the HTTP
* method in the "Access-Control-Request-Method" header.
*/
@Nullable
private RequestMethodsRequestCondition matchPreFlight(HttpServletRequest request) {
if (getMethods().isEmpty()) {
return this;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -17,7 +17,6 @@
package org.springframework.web.servlet.mvc.method;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpMethod;
@@ -407,17 +406,17 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
private static class DefaultBuilder implements Builder {
private String[] paths;
private String[] paths = new String[0];
private RequestMethod[] methods;
private RequestMethod[] methods = new RequestMethod[0];
private String[] params;
private String[] params = new String[0];
private String[] headers;
private String[] headers = new String[0];
private String[] consumes;
private String[] consumes = new String[0];
private String[] produces;
private String[] produces = new String[0];
private String mappingName;
@@ -493,7 +492,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
this.options.getFileExtensions());
return new RequestMappingInfo(this.mappingName, patternsCondition,
new RequestMethodsRequestCondition(methods),
new RequestMethodsRequestCondition(this.methods),
new ParamsRequestCondition(this.params),
new HeadersRequestCondition(this.headers),
new ConsumesRequestCondition(this.consumes, this.headers),
@@ -536,7 +535,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
/**
* Return a custom UrlPathHelper to use for the PatternsRequestCondition, if any.
*/
@Nullable
public UrlPathHelper getUrlPathHelper() {
return this.urlPathHelper;
}
@@ -613,6 +612,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
* {@code registeredSuffixPatternMatch=true}, the extensions are obtained
* from the configured {@code contentNegotiationManager}.
*/
@Nullable
public List<String> getFileExtensions() {
if (useRegisteredSuffixPatternMatch() && getContentNegotiationManager() != null) {
return this.contentNegotiationManager.getAllFileExtensions();

View File

@@ -23,6 +23,7 @@ import org.springframework.http.converter.json.AbstractJackson2HttpMessageConver
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
/**
* A convenient base class for {@code ResponseBodyAdvice} implementations
@@ -41,10 +42,13 @@ public abstract class AbstractMappingJacksonResponseBodyAdvice implements Respon
}
@Override
public final Object beforeBodyWrite(Object body, MethodParameter returnType,
public final Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType,
MediaType contentType, Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (body == null) {
return null;
}
MappingJacksonValue container = getOrCreateContainer(body);
beforeBodyWriteInternal(container, contentType, returnType, request, response);
return container;

View File

@@ -29,7 +29,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
@@ -95,7 +94,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
* @since 4.2
*/
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
List<Object> requestResponseBodyAdvice) {
@Nullable List<Object> requestResponseBodyAdvice) {
Assert.notEmpty(converters, "'messageConverters' must not be empty");
this.messageConverters = converters;
@@ -124,7 +123,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
* {@link RequestBodyAdvice} where each instance may be wrapped as a
* {@link org.springframework.web.method.ControllerAdviceBean ControllerAdviceBean}.
*/
protected RequestResponseBodyAdviceChain getAdvice() {
RequestResponseBodyAdviceChain getAdvice() {
return this.advice;
}
@@ -139,7 +138,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
* @throws IOException if the reading from the request fails
* @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
*/
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, @Nullable MethodParameter parameter,
@Nullable
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
HttpInputMessage inputMessage = createInputMessage(webRequest);
@@ -151,7 +151,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
* from the given HttpInputMessage.
* @param <T> the expected type of the argument value to be created
* @param inputMessage the HTTP input message representing the current request
* @param parameter the method parameter descriptor (may be {@code null})
* @param parameter the method parameter descriptor
* @param targetType the target type, not necessarily the same as the method
* parameter type, e.g. for {@code HttpEntity<String>}.
* @return the created method argument value
@@ -160,7 +160,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
*/
@SuppressWarnings("unchecked")
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, @Nullable MethodParameter parameter,
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
MediaType contentType;
@@ -176,54 +176,40 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
Class<?> contextClass = (parameter != null ? parameter.getContainingClass() : null);
Class<?> contextClass = parameter.getContainingClass();
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = (parameter != null ?
ResolvableType.forMethodParameter(parameter) : ResolvableType.forType(targetType));
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
HttpMethod httpMethod = ((HttpRequest) inputMessage).getMethod();
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;
EmptyBodyCheckingHttpInputMessage message;
try {
inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
if (inputMessage.getBody() != null) {
inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
body = genericConverter.read(targetType, contextClass, inputMessage);
body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
}
break;
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
}
else if (targetClass != null) {
if (converter.canRead(targetClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
if (inputMessage.getBody() != null) {
inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
}
break;
if (message.hasBody()) {
HttpInputMessage inputMessageToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, message) :
((HttpMessageConverter<T>) converter).read(targetClass, message));
body = getAdvice().afterBodyRead(body, inputMessageToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
@@ -233,7 +219,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && inputMessage.getBody() == null)) {
(noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
@@ -249,6 +235,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
*/
protected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
return new ServletServerHttpRequest(servletRequest);
}
@@ -284,7 +271,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
*/
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
int i = parameter.getParameterIndex();
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult;
}
@@ -296,7 +283,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
* @return the adapted argument, or the original resolved argument as-is
* @since 4.3.5
*/
protected Object adaptArgumentIfNecessary(Object arg, MethodParameter parameter) {
@Nullable
protected Object adaptArgumentIfNecessary(@Nullable Object arg, MethodParameter parameter) {
if (parameter.getParameterType() == Optional.class) {
if (arg == null || (arg instanceof Collection && ((Collection) arg).isEmpty()) ||
(arg instanceof Object[] && ((Object[]) arg).length == 0)) {
@@ -316,16 +304,10 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
private final InputStream body;
private final HttpMethod method;
public EmptyBodyCheckingHttpInputMessage(HttpInputMessage inputMessage) throws IOException {
this.headers = inputMessage.getHeaders();
InputStream inputStream = inputMessage.getBody();
if (inputStream == null) {
this.body = null;
}
else if (inputStream.markSupported()) {
if (inputStream.markSupported()) {
inputStream.mark(1);
this.body = (inputStream.read() != -1 ? inputStream : null);
inputStream.reset();
@@ -341,7 +323,6 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
pushbackInputStream.unread(b);
}
}
this.method = ((HttpRequest) inputMessage).getMethod();
}
@Override
@@ -350,12 +331,12 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
}
@Override
public InputStream getBody() throws IOException {
public InputStream getBody() {
return this.body;
}
public HttpMethod getMethod() {
return this.method;
public boolean hasBody() {
return (this.body != null);
}
}

View File

@@ -41,6 +41,7 @@ import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
@@ -74,13 +75,14 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application");
private static final UrlPathHelper DECODING_URL_PATH_HELPER = new UrlPathHelper();
private static final UrlPathHelper RAW_URL_PATH_HELPER = new UrlPathHelper();
private static final UrlPathHelper decodingUrlPathHelper = new UrlPathHelper();
private static final UrlPathHelper rawUrlPathHelper = new UrlPathHelper();
static {
RAW_URL_PATH_HELPER.setRemoveSemicolonContent(false);
RAW_URL_PATH_HELPER.setUrlDecode(false);
rawUrlPathHelper.setRemoveSemicolonContent(false);
rawUrlPathHelper.setUrlDecode(false);
}
@@ -95,14 +97,14 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
* Constructor with list of converters only.
*/
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters) {
this(converters, null);
this(converters, null, null);
}
/**
* Constructor with list of converters and ContentNegotiationManager.
*/
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters,
ContentNegotiationManager contentNegotiationManager) {
@Nullable ContentNegotiationManager contentNegotiationManager) {
this(converters, contentNegotiationManager, null);
}
@@ -112,7 +114,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
* as request/response body advice instances.
*/
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters,
ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
@Nullable ContentNegotiationManager manager, @Nullable List<Object> requestResponseBodyAdvice) {
super(converters, requestResponseBodyAdvice);
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
@@ -135,6 +137,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
*/
protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequest) {
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Assert.state(response != null, "No HttpServletResponse");
return new ServletServerHttpResponse(response);
}
@@ -161,7 +164,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
* by the {@code Accept} header on the request cannot be met by the message converters
*/
@SuppressWarnings("unchecked")
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
@@ -185,7 +188,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
if (outputValue != null && producibleMediaTypes.isEmpty()) {
throw new IllegalArgumentException("No converter found for return value of type: " + valueType);
throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);
}
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<>();
@@ -267,7 +270,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
* return type needs to be examined possibly including generic type determination
* (e.g. {@code ResponseEntity<T>}).
*/
protected Class<?> getReturnValueType(Object value, MethodParameter returnType) {
protected Class<?> getReturnValueType(@Nullable Object value, MethodParameter returnType) {
return (value != null ? value.getClass() : returnType.getParameterType());
}
@@ -275,9 +278,10 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
* Return the generic type of the {@code returnType} (or of the nested type
* if it is an {@link HttpEntity}).
*/
@Nullable
private Type getGenericType(MethodParameter returnType) {
if (HttpEntity.class.isAssignableFrom(returnType.getParameterType())) {
return ResolvableType.forType(returnType.getGenericParameterType()).getGeneric(0).getType();
return ResolvableType.forType(returnType.getGenericParameterType()).getGeneric().getType();
}
else {
return returnType.getGenericParameterType();
@@ -365,7 +369,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
}
HttpServletRequest servletRequest = request.getServletRequest();
String requestUri = RAW_URL_PATH_HELPER.getOriginatingRequestUri(servletRequest);
String requestUri = rawUrlPathHelper.getOriginatingRequestUri(servletRequest);
int index = requestUri.lastIndexOf('/') + 1;
String filename = requestUri.substring(index);
@@ -377,10 +381,10 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
filename = filename.substring(0, index);
}
filename = DECODING_URL_PATH_HELPER.decodeRequestString(servletRequest, filename);
filename = decodingUrlPathHelper.decodeRequestString(servletRequest, filename);
String ext = StringUtils.getFilenameExtension(filename);
pathParams = DECODING_URL_PATH_HELPER.decodeRequestString(servletRequest, pathParams);
pathParams = decodingUrlPathHelper.decodeRequestString(servletRequest, pathParams);
String extInPathParams = StringUtils.getFilenameExtension(pathParams);
if (!safeExtension(servletRequest, ext) || !safeExtension(servletRequest, extInPathParams)) {
@@ -389,7 +393,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
}
@SuppressWarnings("unchecked")
private boolean safeExtension(HttpServletRequest request, String extension) {
private boolean safeExtension(HttpServletRequest request, @Nullable String extension) {
if (!StringUtils.hasText(extension)) {
return true;
}
@@ -408,13 +412,13 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
return true;
}
}
return safeMediaTypesForExtension(extension);
return safeMediaTypesForExtension(new ServletWebRequest(request), extension);
}
private boolean safeMediaTypesForExtension(String extension) {
private boolean safeMediaTypesForExtension(NativeWebRequest request, String extension) {
List<MediaType> mediaTypes = null;
try {
mediaTypes = this.pathStrategy.resolveMediaTypeKey(null, extension);
mediaTypes = this.pathStrategy.resolveMediaTypeKey(request, extension);
}
catch (HttpMediaTypeNotAcceptableException ex) {
// Ignore

View File

@@ -18,6 +18,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.WebAsyncTask;
import org.springframework.web.context.request.async.WebAsyncUtils;
@@ -46,7 +47,7 @@ public class AsyncTaskMethodReturnValueHandler implements HandlerMethodReturnVal
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {

View File

@@ -19,6 +19,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.util.concurrent.Callable;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
@@ -38,7 +39,7 @@ public class CallableMethodReturnValueHandler implements HandlerMethodReturnValu
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {

View File

@@ -20,6 +20,7 @@ import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.context.request.NativeWebRequest;
@@ -47,7 +48,7 @@ public class DeferredResultMethodReturnValueHandler implements HandlerMethodRetu
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -24,7 +24,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -132,7 +131,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
* Configure the complete list of supported argument types thus overriding
* the resolvers that would otherwise be configured by default.
*/
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
public void setArgumentResolvers(@Nullable List<HandlerMethodArgumentResolver> argumentResolvers) {
if (argumentResolvers == null) {
this.argumentResolvers = null;
}
@@ -172,7 +171,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
* Configure the complete list of supported return value types thus
* overriding handlers that would otherwise be configured by default.
*/
public void setReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
public void setReturnValueHandlers(@Nullable List<HandlerMethodReturnValueHandler> returnValueHandlers) {
if (returnValueHandlers == null) {
this.returnValueHandlers = null;
}
@@ -227,7 +226,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
* but before the body is written to the response with the selected
* {@code HttpMessageConverter}.
*/
public void setResponseBodyAdvice(List<ResponseBodyAdvice<?>> responseBodyAdvice) {
public void setResponseBodyAdvice(@Nullable List<ResponseBodyAdvice<?>> responseBodyAdvice) {
this.responseBodyAdvice.clear();
if (responseBodyAdvice != null) {
this.responseBodyAdvice.addAll(responseBodyAdvice);
@@ -239,6 +238,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
this.applicationContext = applicationContext;
}
@Nullable
public ApplicationContext getApplicationContext() {
return this.applicationContext;
}
@@ -271,14 +271,18 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
AnnotationAwareOrderComparator.sort(adviceBeans);
for (ControllerAdviceBean adviceBean : adviceBeans) {
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
if (logger.isInfoEnabled()) {
logger.info("Detected @ExceptionHandler methods in " + adviceBean);
}
}
if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
if (logger.isInfoEnabled()) {
logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean);
@@ -413,7 +417,6 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;

View File

@@ -52,7 +52,7 @@ public class ExtendedServletRequestDataBinder extends ServletRequestDataBinder {
* @param objectName the name of the target object
* @see #DEFAULT_OBJECT_NAME
*/
public ExtendedServletRequestDataBinder(@Nullable Object target, String objectName) {
public ExtendedServletRequestDataBinder(@Nullable Object target, @Nullable String objectName) {
super(target, objectName);
}

View File

@@ -103,7 +103,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
* {@code ResponseEntity}.
*/
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters,
ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
@Nullable ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
super(converters, manager, requestResponseBodyAdvice);
}
@@ -122,8 +122,8 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory)
throws IOException, HttpMediaTypeNotSupportedException {
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
@@ -164,7 +164,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
mavContainer.setRequestHandled(true);
@@ -221,24 +221,25 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
}
private List<String> getVaryRequestHeadersToAdd(HttpHeaders responseHeaders, HttpHeaders entityHeaders) {
if (!responseHeaders.containsKey(HttpHeaders.VARY)) {
return entityHeaders.getVary();
}
List<String> entityHeadersVary = entityHeaders.getVary();
List<String> result = new ArrayList<>(entityHeadersVary);
for (String header : responseHeaders.get(HttpHeaders.VARY)) {
for (String existing : StringUtils.tokenizeToStringArray(header, ",")) {
if ("*".equals(existing)) {
return Collections.emptyList();
}
for (String value : entityHeadersVary) {
if (value.equalsIgnoreCase(existing)) {
result.remove(value);
List<String> vary = responseHeaders.get(HttpHeaders.VARY);
if (vary != null) {
List<String> result = new ArrayList<>(entityHeadersVary);
for (String header : vary) {
for (String existing : StringUtils.tokenizeToStringArray(header, ",")) {
if ("*".equals(existing)) {
return Collections.emptyList();
}
for (String value : entityHeadersVary) {
if (value.equalsIgnoreCase(existing)) {
result.remove(value);
}
}
}
}
return result;
}
return result;
return entityHeadersVary;
}
private boolean isResourceNotModified(ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) {
@@ -262,15 +263,19 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
if (!CollectionUtils.isEmpty(flashAttributes)) {
HttpServletRequest req = request.getNativeRequest(HttpServletRequest.class);
HttpServletResponse res = request.getNativeRequest(HttpServletResponse.class);
RequestContextUtils.getOutputFlashMap(req).putAll(flashAttributes);
RequestContextUtils.saveOutputFlashMap(location, req, res);
HttpServletResponse res = request.getNativeResponse(HttpServletResponse.class);
if (req != null) {
RequestContextUtils.getOutputFlashMap(req).putAll(flashAttributes);
if (res != null) {
RequestContextUtils.saveOutputFlashMap(location, req, res);
}
}
}
}
}
@Override
protected Class<?> getReturnValueType(Object returnValue, MethodParameter returnType) {
protected Class<?> getReturnValueType(@Nullable Object returnValue, MethodParameter returnType) {
if (returnValue != null) {
return returnValue.getClass();
}

View File

@@ -21,6 +21,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
@@ -41,16 +42,17 @@ public class HttpHeadersReturnValueHandler implements HandlerMethodReturnValueHa
@Override
@SuppressWarnings("resource")
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
mavContainer.setRequestHandled(true);
Assert.isInstanceOf(HttpHeaders.class, returnValue, "HttpHeaders expected");
Assert.state(returnValue instanceof HttpHeaders, "HttpHeaders expected");
HttpHeaders headers = (HttpHeaders) returnValue;
if (!headers.isEmpty()) {
HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);
Assert.state(servletResponse != null, "No HttpServletResponse");
ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(servletResponse);
outputMessage.getHeaders().putAll(headers);
outputMessage.getBody(); // flush headers

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.
@@ -26,6 +26,7 @@ import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonInputMessage;
import org.springframework.util.Assert;
/**
* A {@link RequestBodyAdvice} implementation that adds support for Jackson's
@@ -40,8 +41,6 @@ import org.springframework.http.converter.json.MappingJacksonInputMessage;
* be specified, the use for a request body advice is only supported with
* exactly one class argument. Consider the use of a composite interface.
*
* <p>Jackson 2.5 or later is required for parameter-level use of {@code @JsonView}.
*
* @author Sebastien Deleuze
* @since 4.2
* @see com.fasterxml.jackson.annotation.JsonView
@@ -61,12 +60,15 @@ public class JsonViewRequestBodyAdvice extends RequestBodyAdviceAdapter {
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter,
Type targetType, Class<? extends HttpMessageConverter<?>> selectedConverterType) throws IOException {
JsonView annotation = methodParameter.getParameterAnnotation(JsonView.class);
Class<?>[] classes = annotation.value();
JsonView ann = methodParameter.getParameterAnnotation(JsonView.class);
Assert.state(ann != null, "No JsonView annotation");
Class<?>[] classes = ann.value();
if (classes.length != 1) {
throw new IllegalArgumentException(
"@JsonView only supported for request body advice with exactly 1 class argument: " + methodParameter);
}
return new MappingJacksonInputMessage(inputMessage.getBody(), inputMessage.getHeaders(), classes[0]);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -24,6 +24,7 @@ import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.Assert;
/**
* A {@link ResponseBodyAdvice} implementation that adds support for Jackson's
@@ -54,12 +55,15 @@ public class JsonViewResponseBodyAdvice extends AbstractMappingJacksonResponseBo
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
JsonView annotation = returnType.getMethodAnnotation(JsonView.class);
Class<?>[] classes = annotation.value();
JsonView ann = returnType.getMethodAnnotation(JsonView.class);
Assert.state(ann != null, "No JsonView annotation");
Class<?>[] classes = ann.value();
if (classes.length != 1) {
throw new IllegalArgumentException(
"@JsonView only supported for response body advice with exactly 1 class argument: " + returnType);
}
bodyContainer.setSerializationView(classes[0]);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -22,6 +22,8 @@ import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -58,8 +60,8 @@ public class MatrixVariableMapMethodArgumentResolver implements HandlerMethodArg
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception {
@SuppressWarnings("unchecked")
Map<String, MultiValueMap<String, String>> matrixVariables =
@@ -71,7 +73,9 @@ public class MatrixVariableMapMethodArgumentResolver implements HandlerMethodArg
}
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
String pathVariable = parameter.getParameterAnnotation(MatrixVariable.class).pathVar();
MatrixVariable ann = parameter.getParameterAnnotation(MatrixVariable.class);
Assert.state(ann != null, "No MatrixVariable annotation");
String pathVariable = ann.pathVar();
if (!pathVariable.equals(ValueConstants.DEFAULT_NONE)) {
MultiValueMap<String, String> mapForPathVariable = matrixVariables.get(pathVariable);
@@ -97,7 +101,8 @@ public class MatrixVariableMapMethodArgumentResolver implements HandlerMethodArg
if (!MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
ResolvableType[] genericTypes = ResolvableType.forMethodParameter(parameter).getGenerics();
if (genericTypes.length == 2) {
return !List.class.isAssignableFrom(genericTypes[1].getRawClass());
Class<?> declaredClass = genericTypes[1].getRawClass();
return (declaredClass == null || !List.class.isAssignableFrom(declaredClass));
}
}
return false;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -21,6 +21,7 @@ import java.util.List;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
@@ -55,16 +56,17 @@ public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMeth
return false;
}
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
String variableName = parameter.getParameterAnnotation(MatrixVariable.class).name();
return StringUtils.hasText(variableName);
MatrixVariable matrixVariable = parameter.getParameterAnnotation(MatrixVariable.class);
return (matrixVariable != null && StringUtils.hasText(matrixVariable.name()));
}
return true;
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
MatrixVariable annotation = parameter.getParameterAnnotation(MatrixVariable.class);
return new MatrixVariableNamedValueInfo(annotation);
MatrixVariable ann = parameter.getParameterAnnotation(MatrixVariable.class);
Assert.state(ann != null, "No MatrixVariable annotation");
return new MatrixVariableNamedValueInfo(ann);
}
@Override
@@ -76,7 +78,9 @@ public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMeth
return null;
}
String pathVar = parameter.getParameterAnnotation(MatrixVariable.class).pathVar();
MatrixVariable ann = parameter.getParameterAnnotation(MatrixVariable.class);
Assert.state(ann != null, "No MatrixVariable annotation");
String pathVar = ann.pathVar();
List<String> paramValues = null;
if (!pathVar.equals(ValueConstants.DEFAULT_NONE)) {

View File

@@ -75,7 +75,7 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -20,7 +20,9 @@ import java.lang.reflect.Method;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.ModelAttributeMethodProcessor;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
@@ -61,7 +63,7 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth
/**
* Create a new instance.
*/
public ModelAndViewResolverMethodReturnValueHandler(List<ModelAndViewResolver> mavResolvers) {
public ModelAndViewResolverMethodReturnValueHandler(@Nullable List<ModelAndViewResolver> mavResolvers) {
this.mavResolvers = mavResolvers;
}
@@ -75,13 +77,14 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (this.mavResolvers != null) {
for (ModelAndViewResolver mavResolver : this.mavResolvers) {
Class<?> handlerType = returnType.getContainingClass();
Method method = returnType.getMethod();
Assert.state(method != null, "No handler method");
ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel();
ModelAndView mav = mavResolver.resolveModelAndView(method, handlerType, returnValue, model, webRequest);
if (mav != ModelAndViewResolver.UNRESOLVED) {

View File

@@ -22,7 +22,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.aopalliance.intercept.MethodInterceptor;
@@ -411,7 +410,7 @@ public class MvcUriComponentsBuilder {
* @since 4.2
*/
public static UriComponentsBuilder fromMethod(UriComponentsBuilder baseUrl,
Class<?> controllerType, Method method, Object... args) {
@Nullable Class<?> controllerType, Method method, Object... args) {
return fromMethodInternal(baseUrl,
(controllerType != null ? controllerType : method.getDeclaringClass()), method, args);
@@ -429,7 +428,7 @@ public class MvcUriComponentsBuilder {
return UriComponentsBuilder.newInstance().uriComponents(uriComponents);
}
private static UriComponentsBuilder getBaseUrlToUse(UriComponentsBuilder baseUrl) {
private static UriComponentsBuilder getBaseUrlToUse(@Nullable UriComponentsBuilder baseUrl) {
if (baseUrl != null) {
return baseUrl.cloneBuilder();
}
@@ -741,14 +740,15 @@ public class MvcUriComponentsBuilder {
}
@Override
@Nullable
public Object intercept(Object obj, Method method, Object[] args, @Nullable MethodProxy proxy) {
if (getControllerMethod.equals(method)) {
if (method.equals(getControllerMethod)) {
return this.controllerMethod;
}
else if (getArgumentValues.equals(method)) {
else if (method.equals(getArgumentValues)) {
return this.argumentValues;
}
else if (getControllerType.equals(method)) {
else if (method.equals(getControllerType)) {
return this.controllerType;
}
else if (ReflectionUtils.isObjectMethod(method)) {
@@ -799,7 +799,7 @@ public class MvcUriComponentsBuilder {
/**
* @since 4.2
*/
public MethodArgumentBuilder(UriComponentsBuilder baseUrl, Class<?> controllerType, Method method) {
public MethodArgumentBuilder(@Nullable UriComponentsBuilder baseUrl, Class<?> controllerType, Method method) {
Assert.notNull(controllerType, "'controllerType' is required");
Assert.notNull(method, "'method' is required");
this.baseUrl = (baseUrl != null ? baseUrl : initBaseUrl());
@@ -813,7 +813,8 @@ public class MvcUriComponentsBuilder {
private static UriComponentsBuilder initBaseUrl() {
UriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentServletMapping();
return UriComponentsBuilder.fromPath(builder.build().getPath());
String path = builder.build().getPath();
return (path != null ? UriComponentsBuilder.fromPath(path) : UriComponentsBuilder.newInstance());
}
public MethodArgumentBuilder arg(int index, Object value) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -21,6 +21,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
@@ -45,16 +46,16 @@ public class PathVariableMapMethodArgumentResolver implements HandlerMethodArgum
@Override
public boolean supportsParameter(MethodParameter parameter) {
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
return (ann != null && (Map.class.isAssignableFrom(parameter.getParameterType()))
&& !StringUtils.hasText(ann.value()));
return (ann != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
!StringUtils.hasText(ann.value()));
}
/**
* Return a Map with all URI template variables or an empty map.
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
@SuppressWarnings("unchecked")
Map<String, String> uriTemplateVars =

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -25,6 +25,7 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.ServletRequestBindingException;
@@ -73,16 +74,17 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod
return false;
}
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
String paramName = parameter.getParameterAnnotation(PathVariable.class).value();
return StringUtils.hasText(paramName);
PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
}
return true;
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
PathVariable annotation = parameter.getParameterAnnotation(PathVariable.class);
return new PathVariableNamedValueInfo(annotation);
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
Assert.state(ann != null, "No PathVariable annotation");
return new PathVariableNamedValueInfo(ann);
}
@Override
@@ -100,7 +102,7 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod
@Override
@SuppressWarnings("unchecked")
protected void handleResolvedValue(Object arg, String name, MethodParameter parameter,
protected void handleResolvedValue(@Nullable Object arg, String name, MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer, NativeWebRequest request) {
String key = View.PATH_VARIABLES;
@@ -123,15 +125,13 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
String name = (ann != null && !StringUtils.isEmpty(ann.value()) ? ann.value() : parameter.getParameterName());
value = formatUriValue(conversionService, new TypeDescriptor(parameter.nestedIfOptional()), value);
uriVariables.put(name, value);
String formatted = formatUriValue(conversionService, new TypeDescriptor(parameter.nestedIfOptional()), value);
uriVariables.put(name, formatted);
}
protected String formatUriValue(ConversionService cs, TypeDescriptor sourceType, Object value) {
if (value == null) {
return null;
}
else if (value instanceof String) {
@Nullable
protected String formatUriValue(@Nullable ConversionService cs, @Nullable TypeDescriptor sourceType, Object value) {
if (value instanceof String) {
return (String) value;
}
else if (cs != null) {

View File

@@ -17,6 +17,7 @@
package org.springframework.web.servlet.mvc.method.annotation;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -335,19 +336,27 @@ class ReactiveTypeHandler {
}
}
private SseEmitter.SseEventBuilder adapt(ServerSentEvent<?> event) {
private SseEmitter.SseEventBuilder adapt(ServerSentEvent<?> sse) {
SseEmitter.SseEventBuilder builder = SseEmitter.event();
if (event.id() != null) {
builder.id(event.id());
String id = sse.id();
String event = sse.event();
Duration retry = sse.retry();
String comment = sse.comment();
Object data = sse.data();
if (id != null) {
builder.id(id);
}
if (event.comment() != null) {
builder.comment(event.comment());
if (event != null) {
builder.name(event);
}
if (event.data() != null) {
builder.data(event.data());
if (data != null) {
builder.data(data);
}
if (event.retry() != null) {
builder.reconnectTime(event.retry().toMillis());
if (retry != null) {
builder.reconnectTime(retry.toMillis());
}
if (comment != null) {
builder.comment(comment);
}
return builder;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -19,8 +19,10 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
@@ -50,11 +52,13 @@ public class RedirectAttributesMethodArgumentResolver implements HandlerMethodAr
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "RedirectAttributes argument only supported on regular handler methods");
ModelMap redirectAttributes;
if(binderFactory != null) {
if (binderFactory != null) {
DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null);
redirectAttributes = new RedirectAttributesModelMap(dataBinder);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -19,6 +19,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
import javax.servlet.ServletException;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.ValueConstants;
@@ -42,6 +43,7 @@ public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueMe
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestAttribute ann = parameter.getParameterAnnotation(RequestAttribute.class);
Assert.state(ann != null, "No RequestAttribute annotation");
return new NamedValueInfo(ann.name(), ann.required(), ValueConstants.DEFAULT_NONE);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.
@@ -50,21 +50,6 @@ public interface RequestBodyAdvice {
boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType);
/**
* Invoked second (and last) if the body is empty.
* @param body set to {@code null} before the first advice is called
* @param inputMessage the request
* @param parameter the method parameter
* @param targetType the target type, not necessarily the same as the method
* parameter type, e.g. for {@code HttpEntity<String>}.
* @param converterType the selected converter type
* @return the value to use or {@code null} which may then raise an
* {@code HttpMessageNotReadableException} if the argument is required.
*/
@Nullable
Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
/**
* Invoked second before the request body is read and converted.
* @param inputMessage the request
@@ -79,7 +64,7 @@ public interface RequestBodyAdvice {
/**
* Invoked third (and last) after the request body is converted to an Object.
* @param body set to the converter Object before the 1st advice is called
* @param body set to the converter Object before the first advice is called
* @param inputMessage the request
* @param parameter the target method parameter
* @param targetType the target type, not necessarily the same as the method
@@ -90,4 +75,20 @@ public interface RequestBodyAdvice {
Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
/**
* Invoked second (and last) if the body is empty.
* @param body usually set to {@code null} before the first advice is called
* @param inputMessage the request
* @param parameter the method parameter
* @param targetType the target type, not necessarily the same as the method
* parameter type, e.g. for {@code HttpEntity<String>}.
* @param converterType the selected converter type
* @return the value to use or {@code null} which may then raise an
* {@code HttpMessageNotReadableException} if the argument is required.
*/
@Nullable
Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
}

View File

@@ -36,18 +36,6 @@ import org.springframework.lang.Nullable;
*/
public abstract class RequestBodyAdviceAdapter implements RequestBodyAdvice {
/**
* The default implementation returns the body that was passed in.
*/
@Override
@Nullable
public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage,
MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
/**
* The default implementation returns the InputMessage that was passed in.
*/
@@ -69,4 +57,16 @@ public abstract class RequestBodyAdviceAdapter implements RequestBodyAdvice {
return body;
}
/**
* The default implementation returns the body that was passed in.
*/
@Override
@Nullable
public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage,
MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
}

View File

@@ -25,7 +25,6 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@@ -208,7 +207,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
* Configure the complete list of supported argument types thus overriding
* the resolvers that would otherwise be configured by default.
*/
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
public void setArgumentResolvers(@Nullable List<HandlerMethodArgumentResolver> argumentResolvers) {
if (argumentResolvers == null) {
this.argumentResolvers = null;
}
@@ -224,13 +223,13 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
*/
@Nullable
public List<HandlerMethodArgumentResolver> getArgumentResolvers() {
return (this.argumentResolvers != null) ? this.argumentResolvers.getResolvers() : null;
return (this.argumentResolvers != null ? this.argumentResolvers.getResolvers() : null);
}
/**
* Configure the supported argument types in {@code @InitBinder} methods.
*/
public void setInitBinderArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
public void setInitBinderArgumentResolvers(@Nullable List<HandlerMethodArgumentResolver> argumentResolvers) {
if (argumentResolvers == null) {
this.initBinderArgumentResolvers = null;
}
@@ -270,7 +269,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
* Configure the complete list of supported return value types thus
* overriding handlers that would otherwise be configured by default.
*/
public void setReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
public void setReturnValueHandlers(@Nullable List<HandlerMethodReturnValueHandler> returnValueHandlers) {
if (returnValueHandlers == null) {
this.returnValueHandlers = null;
}
@@ -344,7 +343,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
* request before it is read and converted for {@code @RequestBody} and
* {@code HttpEntity} method arguments.
*/
public void setRequestBodyAdvice(List<RequestBodyAdvice> requestBodyAdvice) {
public void setRequestBodyAdvice(@Nullable List<RequestBodyAdvice> requestBodyAdvice) {
if (requestBodyAdvice != null) {
this.requestResponseBodyAdvice.addAll(requestBodyAdvice);
}
@@ -355,7 +354,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
* response before {@code @ResponseBody} or {@code ResponseEntity} return
* values are written to the response body.
*/
public void setResponseBodyAdvice(List<ResponseBodyAdvice<?>> responseBodyAdvice) {
public void setResponseBodyAdvice(@Nullable List<ResponseBodyAdvice<?>> responseBodyAdvice) {
if (responseBodyAdvice != null) {
this.requestResponseBodyAdvice.addAll(responseBodyAdvice);
}
@@ -562,36 +561,40 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
}
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(beans);
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
for (ControllerAdviceBean bean : beans) {
Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(bean, attrMethods);
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
if (logger.isInfoEnabled()) {
logger.info("Detected @ModelAttribute methods in " + bean);
logger.info("Detected @ModelAttribute methods in " + adviceBean);
}
}
Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(bean, binderMethods);
this.initBinderAdviceCache.put(adviceBean, binderMethods);
if (logger.isInfoEnabled()) {
logger.info("Detected @InitBinder methods in " + bean);
logger.info("Detected @InitBinder methods in " + adviceBean);
}
}
if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
requestResponseBodyAdviceBeans.add(bean);
if (RequestBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
if (logger.isInfoEnabled()) {
logger.info("Detected RequestBodyAdvice bean in " + bean);
logger.info("Detected RequestBodyAdvice bean in " + adviceBean);
}
}
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
requestResponseBodyAdviceBeans.add(bean);
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
if (logger.isInfoEnabled()) {
logger.info("Detected ResponseBodyAdvice bean in " + bean);
logger.info("Detected ResponseBodyAdvice bean in " + adviceBean);
}
}
}
@@ -971,7 +974,9 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -20,7 +20,6 @@ import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.EmbeddedValueResolverAware;
@@ -161,6 +160,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
/**
* Return the file extensions to use for suffix pattern matching.
*/
@Nullable
public List<String> getFileExtensions() {
return this.config.getFileExtensions();
}
@@ -250,19 +250,20 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* result of merging annotation attributes within an annotation hierarchy.
*/
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, RequestCondition<?> customCondition) {
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
return RequestMappingInfo
RequestMappingInfo.Builder builder = RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name())
.customCondition(customCondition)
.options(this.config)
.build();
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
return builder.options(this.config).build();
}
/**
@@ -317,7 +318,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
return config.applyPermitDefaultValues();
}
private void updateCorsConfig(CorsConfiguration config, CrossOrigin annotation) {
private void updateCorsConfig(CorsConfiguration config, @Nullable CrossOrigin annotation) {
if (annotation == null) {
return;
}
@@ -352,7 +353,13 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
}
private String resolveCorsAnnotationValue(String value) {
return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value);
if (this.embeddedValueResolver != null) {
String resolved = this.embeddedValueResolver.resolveStringValue(value);
return (resolved != null ? resolved : "");
}
else {
return value;
}
}
}

View File

@@ -22,6 +22,8 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
@@ -106,10 +108,12 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
RequestPart requestPart = parameter.getParameterAnnotation(RequestPart.class);
boolean isRequired = ((requestPart == null || requestPart.required()) && !parameter.isOptional());
@@ -125,21 +129,20 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
try {
HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, name);
arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());
WebDataBinder binder = binderFactory.createBinder(request, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(request, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
catch (MissingServletRequestPartException ex) {
if (isRequired) {
throw ex;
}
}
catch (MultipartException ex) {
catch (MissingServletRequestPartException | MultipartException ex) {
if (isRequired) {
throw ex;
}
@@ -157,7 +160,7 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
return adaptArgumentIfNecessary(arg, parameter);
}
private String getPartName(MethodParameter methodParam, RequestPart requestPart) {
private String getPartName(MethodParameter methodParam, @Nullable RequestPart requestPart) {
String partName = (requestPart != null ? requestPart.name() : "");
if (partName.isEmpty()) {
partName = methodParam.getParameterName();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -32,7 +32,6 @@ import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.ControllerAdviceBean;
/**
* Invokes {@link RequestBodyAdvice} and {@link ResponseBodyAdvice} where each
* instance may be (and is most likely) wrapped with
@@ -52,35 +51,20 @@ class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyA
* Create an instance from a list of objects that are either of type
* {@code ControllerAdviceBean} or {@code RequestBodyAdvice}.
*/
public RequestResponseBodyAdviceChain(List<Object> requestResponseBodyAdvice) {
initAdvice(requestResponseBodyAdvice);
}
private void initAdvice(List<Object> requestResponseBodyAdvice) {
if (requestResponseBodyAdvice == null) {
return;
}
for (Object advice : requestResponseBodyAdvice) {
Class<?> beanType = (advice instanceof ControllerAdviceBean ?
((ControllerAdviceBean) advice).getBeanType() : advice.getClass());
if (RequestBodyAdvice.class.isAssignableFrom(beanType)) {
this.requestBodyAdvice.add(advice);
public RequestResponseBodyAdviceChain(@Nullable List<Object> requestResponseBodyAdvice) {
if (requestResponseBodyAdvice != null) {
for (Object advice : requestResponseBodyAdvice) {
Class<?> beanType = (advice instanceof ControllerAdviceBean ?
((ControllerAdviceBean) advice).getBeanType() : advice.getClass());
if (beanType != null) {
if (RequestBodyAdvice.class.isAssignableFrom(beanType)) {
this.requestBodyAdvice.add(advice);
}
else if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(advice);
}
}
}
else if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(advice);
}
}
}
private List<Object> getAdvice(Class<?> adviceType) {
if (RequestBodyAdvice.class == adviceType) {
return this.requestBodyAdvice;
}
else if (ResponseBodyAdvice.class == adviceType) {
return this.responseBodyAdvice;
}
else {
throw new IllegalArgumentException("Unexpected adviceType: " + adviceType);
}
}
@@ -95,18 +79,6 @@ class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyA
throw new UnsupportedOperationException("Not implemented");
}
@Override
public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
if (advice.supports(parameter, targetType, converterType)) {
body = advice.handleEmptyBody(body, inputMessage, parameter, targetType, converterType);
}
}
return body;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
@@ -132,15 +104,29 @@ class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyA
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType contentType,
public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request, ServerHttpResponse response) {
return processBody(body, returnType, contentType, converterType, request, response);
}
@Override
public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
if (advice.supports(parameter, targetType, converterType)) {
body = advice.handleEmptyBody(body, inputMessage, parameter, targetType, converterType);
}
}
return body;
}
@SuppressWarnings("unchecked")
private <T> Object processBody(Object body, MethodParameter returnType, MediaType contentType,
@Nullable
private <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request, ServerHttpResponse response) {
@@ -175,4 +161,16 @@ class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyA
return result;
}
private List<Object> getAdvice(Class<?> adviceType) {
if (RequestBodyAdvice.class == adviceType) {
return this.requestBodyAdvice;
}
else if (ResponseBodyAdvice.class == adviceType) {
return this.responseBodyAdvice;
}
else {
throw new IllegalArgumentException("Unexpected adviceType: " + adviceType);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -30,6 +30,7 @@ import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
@@ -76,7 +77,7 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
* {@code ResponseBodyAdvice}.
*/
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
ContentNegotiationManager manager) {
@Nullable ContentNegotiationManager manager) {
super(converters, manager);
}
@@ -122,48 +123,54 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
* converter to read the content with.
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
return adaptArgumentIfNecessary(arg, parameter);
}
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, @Nullable MethodParameter parameter,
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
if (arg == null) {
if (checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getMethod().toGenericString());
parameter.getExecutable().toGenericString());
}
}
return arg;
}
protected boolean checkRequired(MethodParameter parameter) {
return (parameter.getParameterAnnotation(RequestBody.class).required() && !parameter.isOptional());
RequestBody requestBody = parameter.getParameterAnnotation(RequestBody.class);
return (requestBody != null && requestBody.required() && !parameter.isOptional());
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

View File

@@ -21,6 +21,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
/**
* Allows customizing the response after the execution of an {@code @ResponseBody}
@@ -58,7 +59,8 @@ public interface ResponseBodyAdvice<T> {
* @param response the current response
* @return the body that was passed in or a modified (possibly new) instance
*/
T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType,
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -162,24 +162,22 @@ public class ResponseBodyEmitter {
sendInternal(object, mediaType);
}
private void sendInternal(Object object, MediaType mediaType) throws IOException {
if (object != null) {
if (this.handler != null) {
try {
this.handler.send(object, mediaType);
}
catch (IOException ex) {
completeWithError(ex);
throw ex;
}
catch (Throwable ex) {
completeWithError(ex);
throw new IllegalStateException("Failed to send " + object, ex);
}
private void sendInternal(Object object, @Nullable MediaType mediaType) throws IOException {
if (this.handler != null) {
try {
this.handler.send(object, mediaType);
}
else {
this.earlySendAttempts.add(new DataWithMediaType(object, mediaType));
catch (IOException ex) {
completeWithError(ex);
throw ex;
}
catch (Throwable ex) {
completeWithError(ex);
throw new IllegalStateException("Failed to send " + object, ex);
}
}
else {
this.earlySendAttempts.add(new DataWithMediaType(object, mediaType));
}
}
@@ -238,7 +236,7 @@ public class ResponseBodyEmitter {
*/
interface Handler {
void send(Object data, MediaType mediaType) throws IOException;
void send(Object data, @Nullable MediaType mediaType) throws IOException;
void complete();
@@ -260,7 +258,7 @@ public class ResponseBodyEmitter {
private final MediaType mediaType;
public DataWithMediaType(Object data, MediaType mediaType) {
public DataWithMediaType(Object data, @Nullable MediaType mediaType) {
this.data = data;
this.mediaType = mediaType;
}
@@ -269,6 +267,7 @@ public class ResponseBodyEmitter {
return this.data;
}
@Nullable
public MediaType getMediaType() {
return this.mediaType;
}

View File

@@ -36,6 +36,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.context.request.NativeWebRequest;
@@ -110,7 +111,7 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
@@ -119,6 +120,7 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur
}
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Assert.state(response != null, "No HttpServletResponse");
ServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
if (returnValue instanceof ResponseEntity) {
@@ -135,6 +137,7 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur
}
ServletRequest request = webRequest.getNativeRequest(ServletRequest.class);
Assert.state(request != null, "No ServletRequest");
ShallowEtagHeaderFilter.disableContentCaching(request);
ResponseBodyEmitter emitter;
@@ -180,12 +183,12 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur
}
@Override
public void send(Object data, MediaType mediaType) throws IOException {
public void send(Object data, @Nullable MediaType mediaType) throws IOException {
sendInternal(data, mediaType);
}
@SuppressWarnings("unchecked")
private <T> void sendInternal(T data, MediaType mediaType) throws IOException {
private <T> void sendInternal(T data, @Nullable MediaType mediaType) throws IOException {
if (logger.isTraceEnabled()) {
logger.trace("Writing [" + data + "]");
}

View File

@@ -120,6 +120,7 @@ public abstract class ResponseEntityExceptionHandler {
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class
})
@Nullable
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
HttpHeaders headers = new HttpHeaders();
if (ex instanceof HttpRequestMethodNotSupportedException) {
@@ -452,10 +453,10 @@ public abstract class ResponseEntityExceptionHandler {
AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest webRequest) {
if (webRequest instanceof ServletWebRequest) {
ServletWebRequest servletRequest = (ServletWebRequest) webRequest;
HttpServletRequest request = servletRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = servletRequest.getNativeResponse(HttpServletResponse.class);
if (response.isCommitted()) {
ServletWebRequest servletWebRequest = (ServletWebRequest) webRequest;
HttpServletRequest request = servletWebRequest.getRequest();
HttpServletResponse response = servletWebRequest.getResponse();
if (response != null && response.isCommitted()) {
if (logger.isErrorEnabled()) {
logger.error("Async timeout for " + request.getMethod() + " [" + request.getRequestURI() + "]");
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -21,6 +21,8 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.AbstractCookieValueMethodArgumentResolver;
import org.springframework.web.util.UrlPathHelper;
@@ -38,7 +40,7 @@ public class ServletCookieValueMethodArgumentResolver extends AbstractCookieValu
private UrlPathHelper urlPathHelper = new UrlPathHelper();
public ServletCookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
public ServletCookieValueMethodArgumentResolver(@Nullable ConfigurableBeanFactory beanFactory) {
super(beanFactory);
}
@@ -51,6 +53,8 @@ public class ServletCookieValueMethodArgumentResolver extends AbstractCookieValu
@Override
protected Object resolveName(String cookieName, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);
if (Cookie.class.isAssignableFrom(parameter.getNestedParameterType())) {
return cookieValue;

View File

@@ -21,10 +21,12 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.concurrent.Callable;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ResponseBody;
@@ -131,12 +133,15 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
return;
}
String reason = getResponseStatusReason();
if (StringUtils.hasText(reason)) {
webRequest.getResponse().sendError(status.value(), reason);
}
else {
webRequest.getResponse().setStatus(status.value());
HttpServletResponse response = webRequest.getResponse();
if (response != null) {
String reason = getResponseStatusReason();
if (StringUtils.hasText(reason)) {
response.sendError(status.value(), reason);
}
else {
response.setStatus(status.value());
}
}
// To be picked up by RedirectView
@@ -152,7 +157,7 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
return webRequest.isNotModified();
}
private String getReturnValueHandlingErrorMessage(String message, Object returnValue) {
private String getReturnValueHandlingErrorMessage(String message, @Nullable Object returnValue) {
StringBuilder sb = new StringBuilder(message);
if (returnValue != null) {
sb.append(" [type=").append(returnValue.getClass().getName()).append("]");
@@ -213,7 +218,7 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
* async return type, e.g. Foo instead of {@code DeferredResult<Foo>}.
*/
@Override
public MethodParameter getReturnValueType(Object returnValue) {
public MethodParameter getReturnValueType(@Nullable Object returnValue) {
return this.returnType;
}
@@ -266,7 +271,7 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
return this.returnValue.getClass();
}
if (!ResolvableType.NONE.equals(this.returnType)) {
return this.returnType.resolve();
return this.returnType.resolve(Object.class);
}
return super.getParameterType();
}

View File

@@ -18,7 +18,6 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.util.Collections;
import java.util.Map;
import javax.servlet.ServletRequest;
import org.springframework.core.MethodParameter;
@@ -26,6 +25,7 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.ServletRequestDataBinder;
@@ -152,6 +152,7 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
@Override
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
Assert.state(servletRequest != null, "No ServletRequest");
ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
servletBinder.bind(servletRequest);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2017 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.
@@ -38,7 +38,9 @@ public class ServletRequestDataBinderFactory extends InitBinderDataBinderFactory
* @param binderMethods one or more {@code @InitBinder} methods
* @param initializer provides global data binder initialization
*/
public ServletRequestDataBinderFactory(List<InvocableHandlerMethod> binderMethods, WebBindingInitializer initializer) {
public ServletRequestDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
@Nullable WebBindingInitializer initializer) {
super(binderMethods, initializer);
}
@@ -46,7 +48,9 @@ public class ServletRequestDataBinderFactory extends InitBinderDataBinderFactory
* Returns an instance of {@link ExtendedServletRequestDataBinder}.
*/
@Override
protected ServletRequestDataBinder createBinderInstance(@Nullable Object target, String objectName, NativeWebRequest request) {
protected ServletRequestDataBinder createBinderInstance(
@Nullable Object target, @Nullable String objectName, NativeWebRequest request) throws Exception {
return new ExtendedServletRequestDataBinder(target, objectName);
}

View File

@@ -30,6 +30,7 @@ import javax.servlet.http.HttpSession;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
@@ -86,8 +87,8 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
@@ -118,6 +119,7 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
return nativeRequest;
}
@Nullable
private Object resolveArgument(Class<?> paramType, HttpServletRequest request) throws IOException {
if (HttpSession.class.isAssignableFrom(paramType)) {
HttpSession session = request.getSession();

View File

@@ -22,6 +22,7 @@ import java.io.Writer;
import javax.servlet.ServletResponse;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@@ -57,8 +58,8 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
* {@code null}, the request is considered directly handled.
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
if (mavContainer != null) {
mavContainer.setRequestHandled(true);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2017 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.mvc.method.annotation;
import org.springframework.util.Assert;
import org.springframework.web.bind.support.WebArgumentResolver;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
@@ -45,10 +46,8 @@ public class ServletWebArgumentResolverAdapter extends AbstractWebArgumentResolv
@Override
protected NativeWebRequest getWebRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
return new ServletWebRequest(servletRequestAttributes.getRequest());
}
return null;
Assert.state(requestAttributes instanceof ServletRequestAttributes, "No ServletRequestAttributes");
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
return new ServletWebRequest(servletRequestAttributes.getRequest());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -19,6 +19,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
import javax.servlet.ServletException;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.bind.annotation.ValueConstants;
@@ -42,6 +43,7 @@ public class SessionAttributeMethodArgumentResolver extends AbstractNamedValueMe
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
SessionAttribute ann = parameter.getParameterAnnotation(SessionAttribute.class);
Assert.state(ann != null, "No SessionAttribute annotation");
return new NamedValueInfo(ann.name(), ann.required(), ValueConstants.DEFAULT_NONE);
}

View File

@@ -105,16 +105,13 @@ public class SseEmitter extends ResponseBodyEmitter {
*/
@Override
public void send(Object object, @Nullable MediaType mediaType) throws IOException {
if (object != null) {
send(event().data(object, mediaType));
}
send(event().data(object, mediaType));
}
/**
* Send an SSE event prepared with the given builder. For example:
* <pre>
* // static import of SseEmitter
*
* SseEmitter emitter = new SseEmitter();
* emitter.send(event().name("update").id("1").data(myObject));
* </pre>
@@ -146,26 +143,26 @@ public class SseEmitter extends ResponseBodyEmitter {
*/
public interface SseEventBuilder {
/**
* Add an SSE "comment" line.
*/
SseEventBuilder comment(String comment);
/**
* Add an SSE "event" line.
*/
SseEventBuilder name(String eventName);
/**
* Add an SSE "id" line.
*/
SseEventBuilder id(String id);
/**
* Add an SSE "event" line.
*/
SseEventBuilder name(String eventName);
/**
* Add an SSE "event" line.
*/
SseEventBuilder reconnectTime(long reconnectTimeMillis);
/**
* Add an SSE "comment" line.
*/
SseEventBuilder comment(String comment);
/**
* Add an SSE "data" line.
*/
@@ -195,20 +192,14 @@ public class SseEmitter extends ResponseBodyEmitter {
private StringBuilder sb;
@Override
public SseEventBuilder comment(String comment) {
append(":").append(comment != null ? comment : "").append("\n");
public SseEventBuilder id(String id) {
append("id:").append(id).append("\n");
return this;
}
@Override
public SseEventBuilder name(String name) {
append("event:").append(name != null ? name : "").append("\n");
return this;
}
@Override
public SseEventBuilder id(String id) {
append("id:").append(id != null ? id : "").append("\n");
append("event:").append(name).append("\n");
return this;
}
@@ -218,6 +209,12 @@ public class SseEmitter extends ResponseBodyEmitter {
return this;
}
@Override
public SseEventBuilder comment(String comment) {
append(":").append(comment).append("\n");
return this;
}
@Override
public SseEventBuilder data(Object object) {
return data(object, null);

View File

@@ -26,6 +26,7 @@ import org.springframework.core.ResolvableType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.WebAsyncUtils;
@@ -56,7 +57,7 @@ public class StreamingResponseBodyReturnValueHandler implements HandlerMethodRet
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
@@ -65,6 +66,7 @@ public class StreamingResponseBodyReturnValueHandler implements HandlerMethodRet
}
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Assert.state(response != null, "No HttpServletResponse");
ServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
if (returnValue instanceof ResponseEntity) {
@@ -80,6 +82,7 @@ public class StreamingResponseBodyReturnValueHandler implements HandlerMethodRet
}
ServletRequest request = webRequest.getNativeRequest(ServletRequest.class);
Assert.state(request != null, "No ServletRequest");
ShallowEtagHeaderFilter.disableContentCaching(request);
Assert.isInstanceOf(StreamingResponseBody.class, returnValue, "StreamingResponseBody expected");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.
@@ -19,6 +19,8 @@ package org.springframework.web.servlet.mvc.method.annotation;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@@ -38,7 +40,6 @@ import org.springframework.web.util.UriComponentsBuilder;
*/
public class UriComponentsBuilderMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> type = parameter.getParameterType();
@@ -46,10 +47,11 @@ public class UriComponentsBuilderMethodArgumentResolver implements HandlerMethod
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(request != null, "No HttpServletRequest");
return ServletUriComponentsBuilder.fromServletMapping(request);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -17,6 +17,7 @@
package org.springframework.web.servlet.mvc.method.annotation;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
@@ -46,7 +47,7 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.
@@ -76,7 +76,7 @@ public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValu
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {

View File

@@ -18,7 +18,6 @@ package org.springframework.web.servlet.mvc.support;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -208,7 +207,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
List<MediaType> mediaTypes = ex.getSupportedMediaTypes();
@@ -232,7 +231,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
return new ModelAndView();
@@ -252,7 +251,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @since 4.2
*/
protected ModelAndView handleMissingPathVariable(MissingPathVariableException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage());
return new ModelAndView();
@@ -271,7 +270,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
return new ModelAndView();
@@ -289,7 +288,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleServletRequestBindingException(ServletRequestBindingException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
return new ModelAndView();
@@ -307,7 +306,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleConversionNotSupported(ConversionNotSupportedException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
if (logger.isWarnEnabled()) {
logger.warn("Failed to convert request element: " + ex);
@@ -328,7 +327,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleTypeMismatch(TypeMismatchException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
if (logger.isWarnEnabled()) {
logger.warn("Failed to bind request element: " + ex);
@@ -351,7 +350,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
if (logger.isWarnEnabled()) {
logger.warn("Failed to read HTTP message: " + ex);
@@ -374,7 +373,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleHttpMessageNotWritable(HttpMessageNotWritableException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
if (logger.isWarnEnabled()) {
logger.warn("Failed to write HTTP message: " + ex);
@@ -394,7 +393,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleMethodArgumentNotValidException(MethodArgumentNotValidException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return new ModelAndView();
@@ -411,7 +410,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleMissingServletRequestPartException(MissingServletRequestPartException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
return new ModelAndView();
@@ -429,7 +428,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
* @throws IOException potentially thrown from response.sendError()
*/
protected ModelAndView handleBindException(BindException ex, HttpServletRequest request,
HttpServletResponse response, Object handler) throws IOException {
HttpServletResponse response, @Nullable Object handler) throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return new ModelAndView();

Some files were not shown because too many files have changed in this diff Show More