Support for Servlet 4.0 (PushBuilder argument, MockServletContext)

Issue: SPR-12674
This commit is contained in:
Juergen Hoeller
2017-02-13 15:06:59 +01:00
parent d44325ec91
commit 199aa776c9
32 changed files with 237 additions and 272 deletions

View File

@@ -16,8 +16,10 @@
package org.springframework.web.servlet.mvc.method.annotation;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Method;
import java.security.Principal;
import java.time.ZoneId;
import java.util.Locale;
@@ -28,6 +30,8 @@ import javax.servlet.http.HttpSession;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
@@ -43,13 +47,14 @@ import org.springframework.web.servlet.support.RequestContextUtils;
* <li>{@link ServletRequest}
* <li>{@link MultipartRequest}
* <li>{@link HttpSession}
* <li>{@link PushBuilder} (as of Spring 5.0 on Servlet 4.0)
* <li>{@link Principal}
* <li>{@link InputStream}
* <li>{@link Reader}
* <li>{@link HttpMethod} (as of Spring 4.0)</li>
* <li>{@link HttpMethod} (as of Spring 4.0)
* <li>{@link Locale}
* <li>{@link TimeZone} (as of Spring 4.0)
* <li>{@link java.time.ZoneId} (as of Spring 4.0 and Java 8)</li>
* <li>{@link java.time.ZoneId} (as of Spring 4.0 and Java 8)
* </ul>
*
* @author Arjen Poutsma
@@ -59,6 +64,10 @@ import org.springframework.web.servlet.support.RequestContextUtils;
*/
public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
private static final Method getPushBuilderMethod =
ClassUtils.getMethodIfAvailable(HttpServletRequest.class, "getPushBuilder");
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
@@ -66,6 +75,7 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(getPushBuilderMethod != null && getPushBuilderMethod.getReturnType().isAssignableFrom(paramType)) ||
Principal.class.isAssignableFrom(paramType) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
@@ -80,6 +90,8 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
// WebRequest / NativeWebRequest / ServletWebRequest
if (WebRequest.class.isAssignableFrom(paramType)) {
if (!paramType.isInstance(webRequest)) {
throw new IllegalStateException(
@@ -88,16 +100,26 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
return webRequest;
}
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
Object nativeRequest = webRequest.getNativeRequest(paramType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + paramType.getName() + "]: " + request);
}
return nativeRequest;
return resolveNativeRequest(webRequest, paramType);
}
else if (HttpSession.class.isAssignableFrom(paramType)) {
// HttpServletRequest required for all further argument types
return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
}
private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeRequest = webRequest.getNativeRequest(requiredType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + requiredType.getName() + "]: " + webRequest);
}
return nativeRequest;
}
private Object resolveArgument(Class<?> paramType, HttpServletRequest request) throws IOException {
if (HttpSession.class.isAssignableFrom(paramType)) {
HttpSession session = request.getSession();
if (!paramType.isInstance(session)) {
throw new IllegalStateException(
@@ -105,6 +127,14 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
}
return session;
}
else if (getPushBuilderMethod != null && getPushBuilderMethod.getReturnType().isAssignableFrom(paramType)) {
Object pushBuilder = ReflectionUtils.invokeMethod(getPushBuilderMethod, request);
if (!paramType.isInstance(pushBuilder)) {
throw new IllegalStateException(
"Current push builder is not of type [" + paramType.getName() + "]: " + pushBuilder);
}
return pushBuilder;
}
else if (InputStream.class.isAssignableFrom(paramType)) {
InputStream inputStream = request.getInputStream();
if (!paramType.isInstance(inputStream)) {
@@ -143,11 +173,9 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
TimeZone timeZone = RequestContextUtils.getTimeZone(request);
return (timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault());
}
else {
// Should never happen...
throw new UnsupportedOperationException(
"Unknown parameter type [" + paramType.getName() + "] in " + parameter.getMethod());
}
// Should never happen...
throw new UnsupportedOperationException("Unknown parameter type: " + paramType.getName());
}
}

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.
@@ -16,11 +16,10 @@
package org.springframework.web.servlet.mvc.method.annotation;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.reflect.Method;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
@@ -38,6 +37,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.1
*/
public class ServletResponseMethodArgumentResolver implements HandlerMethodArgumentResolver {
@@ -64,28 +64,36 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
mavContainer.setRequestHandled(true);
}
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Class<?> paramType = parameter.getParameterType();
// ServletResponse, HttpServletResponse
if (ServletResponse.class.isAssignableFrom(paramType)) {
Object nativeResponse = webRequest.getNativeResponse(paramType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + paramType.getName() + "]: " + response);
}
return nativeResponse;
return resolveNativeResponse(webRequest, paramType);
}
else if (OutputStream.class.isAssignableFrom(paramType)) {
// ServletResponse required for all further argument types
return resolveArgument(paramType, resolveNativeResponse(webRequest, ServletResponse.class));
}
private <T> T resolveNativeResponse(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeResponse = webRequest.getNativeResponse(requiredType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + requiredType.getName() + "]: " + webRequest);
}
return nativeResponse;
}
private Object resolveArgument(Class<?> paramType, ServletResponse response) throws IOException {
if (OutputStream.class.isAssignableFrom(paramType)) {
return response.getOutputStream();
}
else if (Writer.class.isAssignableFrom(paramType)) {
return response.getWriter();
}
else {
// should not happen
Method method = parameter.getMethod();
throw new UnsupportedOperationException("Unknown parameter type: " + paramType + " in method: " + method);
}
// Should never happen...
throw new UnsupportedOperationException("Unknown parameter type: " + paramType);
}
}