From ae0b7c26c56cbe519f8ec9ab7dbd780d93b6bf3f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 4 Jul 2016 23:31:21 +0200 Subject: [PATCH] Drop Servlet 2.5 runtime compatibility Issue: SPR-13189 --- .../server/ServletServerHttpResponse.java | 7 +- .../bind/support/WebRequestDataBinder.java | 67 +++++++---------- .../context/request/ServletWebRequest.java | 12 +-- .../async/NoSupportAsyncWebRequest.java | 75 ------------------- .../context/request/async/WebAsyncUtils.java | 20 +---- .../web/filter/ShallowEtagHeaderFilter.java | 28 +++---- .../support/MultipartResolutionDelegate.java | 41 ++-------- .../web/util/CookieGenerator.java | 3 +- .../web/servlet/FrameworkServlet.java | 11 +-- .../servlet/support/WebContentGenerator.java | 8 +- 10 files changed, 56 insertions(+), 216 deletions(-) delete mode 100644 spring-web/src/main/java/org/springframework/web/context/request/async/NoSupportAsyncWebRequest.java diff --git a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java index 5944297feb..a6d1189c3d 100644 --- a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java @@ -39,11 +39,6 @@ import org.springframework.util.CollectionUtils; */ public class ServletServerHttpResponse implements ServerHttpResponse { - /** Checking for Servlet 3.0+ HttpServletResponse.getHeader(String) */ - private static final boolean servlet3Present = - ClassUtils.hasMethod(HttpServletResponse.class, "getHeader", String.class); - - private final HttpServletResponse servletResponse; private final HttpHeaders headers; @@ -60,7 +55,7 @@ public class ServletServerHttpResponse implements ServerHttpResponse { public ServletServerHttpResponse(HttpServletResponse servletResponse) { Assert.notNull(servletResponse, "HttpServletResponse must not be null"); this.servletResponse = servletResponse; - this.headers = (servlet3Present ? new ServletResponseHttpHeaders() : new HttpHeaders()); + this.headers = new ServletResponseHttpHeaders(); } diff --git a/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java b/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java index 94b40ae979..c079c8152d 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java +++ b/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java @@ -22,7 +22,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; import org.springframework.beans.MutablePropertyValues; -import org.springframework.util.ClassUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -116,9 +115,9 @@ public class WebRequestDataBinder extends WebDataBinder { if (multipartRequest != null) { bindMultipart(multipartRequest.getMultiFileMap(), mpvs); } - else if (ClassUtils.hasMethod(HttpServletRequest.class, "getParts")) { - HttpServletRequest serlvetRequest = ((NativeWebRequest) request).getNativeRequest(HttpServletRequest.class); - new Servlet3MultipartHelper(isBindEmptyMultipartFiles()).bindParts(serlvetRequest, mpvs); + else { + HttpServletRequest servletRequest = ((NativeWebRequest) request).getNativeRequest(HttpServletRequest.class); + bindParts(servletRequest, mpvs); } } doBind(mpvs); @@ -133,6 +132,29 @@ public class WebRequestDataBinder extends WebDataBinder { return (contentType != null && StringUtils.startsWithIgnoreCase(contentType, "multipart")); } + private void bindParts(HttpServletRequest request, MutablePropertyValues mpvs) { + try { + MultiValueMap map = new LinkedMultiValueMap(); + for (Part part : request.getParts()) { + map.add(part.getName(), part); + } + for (Map.Entry> entry: map.entrySet()) { + if (entry.getValue().size() == 1) { + Part part = entry.getValue().get(0); + if (isBindEmptyMultipartFiles() || part.getSize() > 0) { + mpvs.add(entry.getKey(), part); + } + } + else { + mpvs.add(entry.getKey(), entry.getValue()); + } + } + } + catch (Exception ex) { + throw new MultipartException("Failed to get request parts", ex); + } + } + /** * Treats errors as fatal. *

Use this method only if it's an error if the input isn't valid. @@ -145,41 +167,4 @@ public class WebRequestDataBinder extends WebDataBinder { } } - - /** - * Encapsulate Part binding code for Servlet 3.0+ only containers. - * @see javax.servlet.http.Part - */ - private static class Servlet3MultipartHelper { - - private final boolean bindEmptyMultipartFiles; - - public Servlet3MultipartHelper(boolean bindEmptyMultipartFiles) { - this.bindEmptyMultipartFiles = bindEmptyMultipartFiles; - } - - public void bindParts(HttpServletRequest request, MutablePropertyValues mpvs) { - try { - MultiValueMap map = new LinkedMultiValueMap(); - for (Part part : request.getParts()) { - map.add(part.getName(), part); - } - for (Map.Entry> entry: map.entrySet()) { - if (entry.getValue().size() == 1) { - Part part = entry.getValue().get(0); - if (this.bindEmptyMultipartFiles || part.getSize() > 0) { - mpvs.add(entry.getKey(), part); - } - } - else { - mpvs.add(entry.getKey(), entry.getValue()); - } - } - } - catch (Exception ex) { - throw new MultipartException("Failed to get request parts", ex); - } - } - } - } diff --git a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java index b29397f214..19b1256eff 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java @@ -23,14 +23,12 @@ import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -73,10 +71,6 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ private static final Pattern ETAG_HEADER_VALUE_PATTERN = Pattern.compile("\\*|\\s*((W\\/)?(\"[^\"]*\"))\\s*,?"); - /** Checking for Servlet 3.0+ HttpServletResponse.getHeader(String) */ - private static final boolean servlet3Present = - ClassUtils.hasMethod(HttpServletResponse.class, "getHeader", String.class); - private boolean notModified = false; @@ -283,19 +277,19 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ private boolean isCompatibleWithConditionalRequests(HttpServletResponse response) { try { - if (response == null || !servlet3Present) { + if (response == null) { // Can't check response.getStatus() - let's assume we're good return true; } return HttpStatus.valueOf(response.getStatus()).is2xxSuccessful(); } - catch (IllegalArgumentException e) { + catch (IllegalArgumentException ex) { return true; } } private boolean isHeaderAbsent(HttpServletResponse response, String header) { - if (response == null || !servlet3Present) { + if (response == null) { // Can't check response.getHeader(header) - let's assume it's not set return true; } diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/NoSupportAsyncWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/async/NoSupportAsyncWebRequest.java deleted file mode 100644 index c9797cd53b..0000000000 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/NoSupportAsyncWebRequest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2015 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.context.request.async; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.web.context.request.ServletWebRequest; - -/** - * An {@code AsyncWebRequest} to use when there is no underlying async support. - * - * @author Rossen Stoyanchev - * @since 3.2 - */ -public class NoSupportAsyncWebRequest extends ServletWebRequest implements AsyncWebRequest { - - public NoSupportAsyncWebRequest(HttpServletRequest request, HttpServletResponse response) { - super(request, response); - } - - - @Override - public void addCompletionHandler(Runnable runnable) { - // ignored - } - - @Override - public void setTimeout(Long timeout) { - // ignored - } - - @Override - public void addTimeoutHandler(Runnable runnable) { - // ignored - } - - @Override - public boolean isAsyncStarted() { - return false; - } - - - // Not supported - - @Override - public void startAsync() { - throw new UnsupportedOperationException("No async support in a pre-Servlet 3.0 runtime"); - } - - @Override - public boolean isAsyncComplete() { - throw new UnsupportedOperationException("No async support in a pre-Servlet 3.0 runtime"); - } - - @Override - public void dispatch() { - throw new UnsupportedOperationException("No async support in a pre-Servlet 3.0 runtime"); - } - -} diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncUtils.java b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncUtils.java index 8690866ebd..774c8aeaf2 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncUtils.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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 javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.springframework.util.ClassUtils; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.WebRequest; @@ -35,9 +34,6 @@ public abstract class WebAsyncUtils { public static final String WEB_ASYNC_MANAGER_ATTRIBUTE = WebAsyncManager.class.getName() + ".WEB_ASYNC_MANAGER"; - // Determine whether Servlet 3.0's ServletRequest.startAsync method is available - private static final boolean startAsyncAvailable = ClassUtils.hasMethod(ServletRequest.class, "startAsync"); - /** * Obtain the {@link WebAsyncManager} for the current request, or if not @@ -76,19 +72,7 @@ public abstract class WebAsyncUtils { * @return an AsyncWebRequest instance (never {@code null}) */ public static AsyncWebRequest createAsyncWebRequest(HttpServletRequest request, HttpServletResponse response) { - return (startAsyncAvailable ? AsyncWebRequestFactory.createStandardAsyncWebRequest(request, response) : - new NoSupportAsyncWebRequest(request, response)); - } - - - /** - * Inner class to avoid a hard dependency on the Servlet 3.0 API. - */ - private static class AsyncWebRequestFactory { - - public static AsyncWebRequest createStandardAsyncWebRequest(HttpServletRequest request, HttpServletResponse response) { - return new StandardServletAsyncWebRequest(request, response); - } + return new StandardServletAsyncWebRequest(request, response); } } diff --git a/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java b/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java index c94791a576..eac685801a 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java @@ -19,7 +19,6 @@ package org.springframework.web.filter; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; - import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; @@ -29,7 +28,6 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpMethod; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; import org.springframework.util.DigestUtils; import org.springframework.web.util.ContentCachingResponseWrapper; import org.springframework.web.util.WebUtils; @@ -61,29 +59,30 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter { private static final String STREAMING_ATTRIBUTE = ShallowEtagHeaderFilter.class.getName() + ".STREAMING"; - /** Checking for Servlet 3.0+ HttpServletResponse.getHeader(String) */ - private static final boolean servlet3Present = - ClassUtils.hasMethod(HttpServletResponse.class, "getHeader", String.class); private boolean writeWeakETag = false; + /** - * Set whether the ETag value written to the response should be weak, as per rfc7232. + * Set whether the ETag value written to the response should be weak, as per RFC 7232. *

Should be configured using an {@code } for parameter name * "writeWeakETag" in the filter definition in {@code web.xml}. - * @see rfc7232 section-2.3 + * @see RFC 7232 section 2.3 + * @since 4.3 */ - public boolean isWriteWeakETag() { - return writeWeakETag; + public void setWriteWeakETag(boolean writeWeakETag) { + this.writeWeakETag = writeWeakETag; } /** - * Return whether the ETag value written to the response should be weak, as per rfc7232. + * Return whether the ETag value written to the response should be weak, as per RFC 7232. + * @since 4.3 */ - public void setWriteWeakETag(boolean writeWeakETag) { - this.writeWeakETag = writeWeakETag; + public boolean isWriteWeakETag() { + return this.writeWeakETag; } + /** * The default value is "false" so that the filter may delay the generation of * an ETag until the last asynchronously dispatched thread. @@ -169,10 +168,7 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter { if (responseStatusCode >= 200 && responseStatusCode < 300 && (HttpMethod.GET.matches(method) || HttpMethod.HEAD.matches(method))) { - String cacheControl = null; - if (servlet3Present) { - cacheControl = response.getHeader(HEADER_CACHE_CONTROL); - } + String cacheControl = response.getHeader(HEADER_CACHE_CONTROL); if (cacheControl == null || !cacheControl.contains(DIRECTIVE_NO_STORE)) { return true; } diff --git a/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java b/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java index 4356d14603..14a412b2f8 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java @@ -24,8 +24,6 @@ import javax.servlet.http.Part; import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.MethodParameter; -import org.springframework.util.ClassUtils; -import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.util.WebUtils; @@ -42,20 +40,6 @@ public abstract class MultipartResolutionDelegate { public static final Object UNRESOLVABLE = new Object(); - private static Class servletPartClass = null; - - static { - try { - servletPartClass = ClassUtils.forName("javax.servlet.http.Part", - MultipartResolutionDelegate.class.getClassLoader()); - } - catch (ClassNotFoundException ex) { - // Servlet 3.0 javax.servlet.http.Part type not available - - // Part references simply not supported then. - } - } - - public static boolean isMultipartRequest(HttpServletRequest request) { return (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null || isMultipartContent(request)); @@ -71,15 +55,7 @@ public abstract class MultipartResolutionDelegate { if (unwrapped != null) { return unwrapped; } - return adaptToMultipartHttpServletRequest(request); - } - - private static MultipartHttpServletRequest adaptToMultipartHttpServletRequest(HttpServletRequest request) { - if (servletPartClass != null) { - // Servlet 3.0 available .. - return new StandardMultipartHttpServletRequest(request); - } - throw new MultipartException("Expected MultipartHttpServletRequest: is a MultipartResolver configured?"); + return new StandardMultipartHttpServletRequest(request); } @@ -87,8 +63,7 @@ public abstract class MultipartResolutionDelegate { Class paramType = parameter.getNestedParameterType(); return (MultipartFile.class == paramType || isMultipartFileCollection(parameter) || isMultipartFileArray(parameter) || - (servletPartClass != null && (servletPartClass == paramType || - isPartCollection(parameter) || isPartArray(parameter)))); + (Part.class == paramType || isPartCollection(parameter) || isPartArray(parameter))); } public static Object resolveMultipartArgument(String name, MethodParameter parameter, HttpServletRequest request) @@ -100,19 +75,19 @@ public abstract class MultipartResolutionDelegate { if (MultipartFile.class == parameter.getNestedParameterType()) { if (multipartRequest == null && isMultipart) { - multipartRequest = adaptToMultipartHttpServletRequest(request); + multipartRequest = new StandardMultipartHttpServletRequest(request); } return (multipartRequest != null ? multipartRequest.getFile(name) : null); } else if (isMultipartFileCollection(parameter)) { if (multipartRequest == null && isMultipart) { - multipartRequest = adaptToMultipartHttpServletRequest(request); + multipartRequest = new StandardMultipartHttpServletRequest(request); } return (multipartRequest != null ? multipartRequest.getFiles(name) : null); } else if (isMultipartFileArray(parameter)) { if (multipartRequest == null && isMultipart) { - multipartRequest = adaptToMultipartHttpServletRequest(request); + multipartRequest = new StandardMultipartHttpServletRequest(request); } if (multipartRequest != null) { List multipartFiles = multipartRequest.getFiles(name); @@ -122,7 +97,7 @@ public abstract class MultipartResolutionDelegate { return null; } } - else if (parameter.getNestedParameterType() == servletPartClass) { + else if (Part.class == parameter.getNestedParameterType()) { return (isMultipart ? RequestPartResolver.resolvePart(request, name) : null); } else if (isPartCollection(parameter)) { @@ -145,11 +120,11 @@ public abstract class MultipartResolutionDelegate { } private static boolean isPartCollection(MethodParameter methodParam) { - return (servletPartClass == getCollectionParameterType(methodParam)); + return (Part.class == getCollectionParameterType(methodParam)); } private static boolean isPartArray(MethodParameter methodParam) { - return (servletPartClass == methodParam.getNestedParameterType().getComponentType()); + return (Part.class == methodParam.getNestedParameterType().getComponentType()); } private static Class getCollectionParameterType(MethodParameter methodParam) { diff --git a/spring-web/src/main/java/org/springframework/web/util/CookieGenerator.java b/spring-web/src/main/java/org/springframework/web/util/CookieGenerator.java index 9e3ba5c08b..b674b3e3c1 100644 --- a/spring-web/src/main/java/org/springframework/web/util/CookieGenerator.java +++ b/spring-web/src/main/java/org/springframework/web/util/CookieGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -145,7 +145,6 @@ public class CookieGenerator { /** * Set whether the cookie is supposed to be marked with the "HttpOnly" attribute. - *

Note that this feature is only available on Servlet 3.0 and higher. * @see javax.servlet.http.Cookie#setHttpOnly */ public void setCookieHttpOnly(boolean cookieHttpOnly) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java index 5b56e210e7..69366940aa 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java @@ -162,11 +162,6 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic private static final String INIT_PARAM_DELIMITERS = ",; \t\n"; - /** Checking for Servlet 3.0+ HttpServletResponse.getStatus() */ - private static final boolean responseGetStatusAvailable = - ClassUtils.hasMethod(HttpServletResponse.class, "getStatus"); - - /** ServletContext attribute to find the WebApplicationContext in */ private String contextAttribute; @@ -912,8 +907,7 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic } } - // Use response wrapper for Servlet 2.5 compatibility where - // the getHeader() method does not exist + // Use response wrapper in order to always add PATCH to the allowed methods super.doOptions(request, new HttpServletResponseWrapper(response) { @Override public void setHeader(String name, String value) { @@ -1069,13 +1063,12 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic if (this.publishEvents) { // Whether or not we succeeded, publish an event. long processingTime = System.currentTimeMillis() - startTime; - int statusCode = (responseGetStatusAvailable ? response.getStatus() : -1); this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), - processingTime, failureCause, statusCode)); + processingTime, failureCause, response.getStatus())); } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java index 6cf54cf292..099459b0a3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java @@ -80,10 +80,6 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport { protected static final String HEADER_CACHE_CONTROL = "Cache-Control"; - /** Checking for Servlet 3.0+ HttpServletResponse.getHeaders(String) */ - private static final boolean servlet3Present = - ClassUtils.hasMethod(HttpServletResponse.class, "getHeaders", String.class); - /** Set of supported HTTP methods */ private Set supportedMethods; @@ -263,8 +259,6 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport { * subject to content negotiation and variances based on the value of the * given request headers. The configured request header names are added only * if not already present in the response "Vary" header. - *

Note: This property is only supported on Servlet 3.0+ - * which allows checking existing response header values. * @param varyByRequestHeaders one or more request header names * @since 4.3 */ @@ -398,7 +392,7 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport { else { applyCacheSeconds(response, this.cacheSeconds); } - if (servlet3Present && this.varyByRequestHeaders != null) { + if (this.varyByRequestHeaders != null) { for (String value : getVaryRequestHeadersToAdd(response)) { response.addHeader("Vary", value); }