From a85bf3185e4f23753989496f0fa5af99b76a0431 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Fri, 16 Feb 2024 15:46:37 +0100 Subject: [PATCH] Remove APIs deprecated for removal in 6.2 This commit removes the following obsolete and deprecated APIs. - org.springframework.util.Base64Utils - org.springframework.cache.jcache.interceptor.JCacheOperationSourcePointcut - org.springframework.http.client.AbstractClientHttpResponse - org.springframework.http.client.ClientHttpResponse.getRawStatusCode() - org.springframework.http.client.observation.ClientHttpObservationDocumentation.HighCardinalityKeyNames.CLIENT_NAME - org.springframework.web.reactive.function.client.ClientHttpObservationDocumentation.HighCardinalityKeyNames.CLIENT_NAME - org.springframework.web.filter.reactive.ServerWebExchangeContextFilter.get(Context) - org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleBindException(...) - org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.handleBindException(...) Closes gh-30608 --- .../JCacheOperationSourcePointcut.java | 68 --------- .../org/springframework/util/Base64Utils.java | 132 ------------------ .../client/AbstractClientHttpResponse.java | 39 ------ .../http/client/ClientHttpResponse.java | 16 +-- .../ClientHttpObservationDocumentation.java | 16 +-- .../ServerWebExchangeContextFilter.java | 17 +-- .../web/client/RestTemplateTests.java | 1 - .../ClientHttpObservationDocumentation.java | 16 +-- .../ResponseEntityExceptionHandler.java | 27 ---- .../DefaultHandlerExceptionResolver.java | 27 ---- .../ResponseEntityExceptionHandlerTests.java | 6 - .../DefaultHandlerExceptionResolverTests.java | 10 -- 12 files changed, 4 insertions(+), 371 deletions(-) delete mode 100644 spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSourcePointcut.java delete mode 100644 spring-core/src/main/java/org/springframework/util/Base64Utils.java delete mode 100644 spring-web/src/main/java/org/springframework/http/client/AbstractClientHttpResponse.java diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSourcePointcut.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSourcePointcut.java deleted file mode 100644 index 34693866eea..00000000000 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheOperationSourcePointcut.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2002-2023 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 - * - * https://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.cache.jcache.interceptor; - -import java.io.Serializable; -import java.lang.reflect.Method; - -import org.springframework.aop.support.StaticMethodMatcherPointcut; -import org.springframework.lang.Nullable; -import org.springframework.util.ObjectUtils; - -/** - * A Pointcut that matches if the underlying {@link JCacheOperationSource} - * has an operation for a given method. - * - * @author Stephane Nicoll - * @since 4.1 - * @deprecated since 6.0.10, as it is not used by the framework anymore - */ -@Deprecated(since = "6.0.10", forRemoval = true) -@SuppressWarnings("serial") -public abstract class JCacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable { - - @Override - public boolean matches(Method method, Class targetClass) { - JCacheOperationSource cas = getCacheOperationSource(); - return (cas != null && cas.getCacheOperation(method, targetClass) != null); - } - - /** - * Obtain the underlying {@link JCacheOperationSource} (may be {@code null}). - * To be implemented by subclasses. - */ - @Nullable - protected abstract JCacheOperationSource getCacheOperationSource(); - - - @Override - public boolean equals(@Nullable Object other) { - return (this == other || (other instanceof JCacheOperationSourcePointcut that && - ObjectUtils.nullSafeEquals(getCacheOperationSource(), that.getCacheOperationSource()))); - } - - @Override - public int hashCode() { - return JCacheOperationSourcePointcut.class.hashCode(); - } - - @Override - public String toString() { - return getClass().getName() + ": " + getCacheOperationSource(); - } - -} diff --git a/spring-core/src/main/java/org/springframework/util/Base64Utils.java b/spring-core/src/main/java/org/springframework/util/Base64Utils.java deleted file mode 100644 index 49cdef9896d..00000000000 --- a/spring-core/src/main/java/org/springframework/util/Base64Utils.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2002-2023 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 - * - * https://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.util; - -import java.util.Base64; - -/** - * A simple utility class for Base64 encoding and decoding. - * - *

Adapts to Java 8's {@link java.util.Base64} in a convenience fashion. - * - * @author Juergen Hoeller - * @author Gary Russell - * @since 4.1 - * @see java.util.Base64 - * @deprecated as of Spring Framework 6.0.5 in favor of {@link Base64}; scheduled - * for removal in 6.2 - */ -@Deprecated(since = "6.0.5", forRemoval = true) -public abstract class Base64Utils { - - /** - * Base64-encode the given byte array. - * @param src the original byte array - * @return the encoded byte array - */ - public static byte[] encode(byte[] src) { - if (src.length == 0) { - return src; - } - return Base64.getEncoder().encode(src); - } - - /** - * Base64-decode the given byte array. - * @param src the encoded byte array - * @return the original byte array - */ - public static byte[] decode(byte[] src) { - if (src.length == 0) { - return src; - } - return Base64.getDecoder().decode(src); - } - - /** - * Base64-encode the given byte array using the RFC 4648 - * "URL and Filename Safe Alphabet". - * @param src the original byte array - * @return the encoded byte array - * @since 4.2.4 - */ - public static byte[] encodeUrlSafe(byte[] src) { - if (src.length == 0) { - return src; - } - return Base64.getUrlEncoder().encode(src); - } - - /** - * Base64-decode the given byte array using the RFC 4648 - * "URL and Filename Safe Alphabet". - * @param src the encoded byte array - * @return the original byte array - * @since 4.2.4 - */ - public static byte[] decodeUrlSafe(byte[] src) { - if (src.length == 0) { - return src; - } - return Base64.getUrlDecoder().decode(src); - } - - /** - * Base64-encode the given byte array to a String. - * @param src the original byte array - * @return the encoded byte array as a UTF-8 String - */ - public static String encodeToString(byte[] src) { - if (src.length == 0) { - return ""; - } - return Base64.getEncoder().encodeToString(src); - } - - /** - * Base64-decode the given byte array from a UTF-8 String. - * @param src the encoded UTF-8 String - * @return the original byte array - */ - public static byte[] decodeFromString(String src) { - if (src.isEmpty()) { - return new byte[0]; - } - return Base64.getDecoder().decode(src); - } - - /** - * Base64-encode the given byte array to a String using the RFC 4648 - * "URL and Filename Safe Alphabet". - * @param src the original byte array - * @return the encoded byte array as a UTF-8 String - */ - public static String encodeToUrlSafeString(byte[] src) { - return Base64.getUrlEncoder().encodeToString(src); - } - - /** - * Base64-decode the given byte array from a UTF-8 String using the RFC 4648 - * "URL and Filename Safe Alphabet". - * @param src the encoded UTF-8 String - * @return the original byte array - */ - public static byte[] decodeFromUrlSafeString(String src) { - return Base64.getUrlDecoder().decode(src); - } - -} diff --git a/spring-web/src/main/java/org/springframework/http/client/AbstractClientHttpResponse.java b/spring-web/src/main/java/org/springframework/http/client/AbstractClientHttpResponse.java deleted file mode 100644 index fdd781a0f94..00000000000 --- a/spring-web/src/main/java/org/springframework/http/client/AbstractClientHttpResponse.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2023 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 - * - * https://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.http.client; - -import java.io.IOException; - -import org.springframework.http.HttpStatusCode; - -/** - * Abstract base for {@link ClientHttpResponse}. - * - * @author Arjen Poutsma - * @since 3.1.1 - * @deprecated as of 6.0, with no direct replacement; scheduled for removal in 6.2 - */ -@Deprecated(since = "6.0", forRemoval = true) -public abstract class AbstractClientHttpResponse implements ClientHttpResponse { - - @Override - @SuppressWarnings("removal") - public HttpStatusCode getStatusCode() throws IOException { - return HttpStatusCode.valueOf(getRawStatusCode()); - } - -} diff --git a/spring-web/src/main/java/org/springframework/http/client/ClientHttpResponse.java b/spring-web/src/main/java/org/springframework/http/client/ClientHttpResponse.java index 7b7f092320f..455f2c12f54 100644 --- a/spring-web/src/main/java/org/springframework/http/client/ClientHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/client/ClientHttpResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -42,20 +42,6 @@ public interface ClientHttpResponse extends HttpInputMessage, Closeable { */ HttpStatusCode getStatusCode() throws IOException; - /** - * Get the HTTP status code as an integer. - * @return the HTTP status as an integer value - * @throws IOException in case of I/O errors - * @since 3.1.1 - * @see #getStatusCode() - * @deprecated as of 6.0, in favor of {@link #getStatusCode()}; scheduled for - * removal in 6.2 - */ - @Deprecated(since = "6.0", forRemoval = true) - default int getRawStatusCode() throws IOException { - return getStatusCode().value(); - } - /** * Get the HTTP status text of the response. * @return the HTTP status text diff --git a/spring-web/src/main/java/org/springframework/http/client/observation/ClientHttpObservationDocumentation.java b/spring-web/src/main/java/org/springframework/http/client/observation/ClientHttpObservationDocumentation.java index d77b46981cb..d4aff8c45cd 100644 --- a/spring-web/src/main/java/org/springframework/http/client/observation/ClientHttpObservationDocumentation.java +++ b/spring-web/src/main/java/org/springframework/http/client/observation/ClientHttpObservationDocumentation.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -135,20 +135,6 @@ public enum ClientHttpObservationDocumentation implements ObservationDocumentati public String asString() { return "http.url"; } - }, - - /** - * Client name derived from the request URI host. - * @deprecated in favor of {@link LowCardinalityKeyNames#CLIENT_NAME}; - * scheduled for removal in 6.2. This will be available both as a low and - * high cardinality key value. - */ - @Deprecated(since = "6.0.5", forRemoval = true) - CLIENT_NAME { - @Override - public String asString() { - return "client.name"; - } } } diff --git a/spring-web/src/main/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilter.java b/spring-web/src/main/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilter.java index 7ff17b72467..5364594447d 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -64,19 +64,4 @@ public class ServerWebExchangeContextFilter implements WebFilter { return contextView.getOrEmpty(EXCHANGE_CONTEXT_ATTRIBUTE); } - /** - * Access the {@link ServerWebExchange} from a Reactor {@link Context}, - * if available, which is generally the case when - * {@link ServerWebExchangeContextFilter} is present in the filter chain. - * @param context the context to get the exchange from - * @return an {@link Optional} with the exchange if found - * @deprecated in favor of using {@link #getExchange(ContextView)} which - * accepts a {@link ContextView} instead of {@link Context}, reflecting the - * fact that the {@code ContextView} is needed only for reading. - */ - @Deprecated(since = "6.0.6", forRemoval = true) - public static Optional get(Context context) { - return context.getOrEmpty(EXCHANGE_CONTEXT_ATTRIBUTE); - } - } diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java index 6cb5e2b52e9..b22d3056426 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java @@ -768,7 +768,6 @@ class RestTemplateTests { given(request.execute()).willReturn(response); given(errorHandler.hasError(response)).willReturn(responseStatus.isError()); given(response.getStatusCode()).willReturn(responseStatus); - given(response.getRawStatusCode()).willReturn(responseStatus.value()); given(response.getStatusText()).willReturn(responseStatus.getReasonPhrase()); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientHttpObservationDocumentation.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientHttpObservationDocumentation.java index e7bbd8af3f4..9f32345dd6f 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientHttpObservationDocumentation.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientHttpObservationDocumentation.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -132,20 +132,6 @@ public enum ClientHttpObservationDocumentation implements ObservationDocumentati public String asString() { return "http.url"; } - }, - - /** - * Client name derived from the request URI host. - * @deprecated in favor of {@link LowCardinalityKeyNames#CLIENT_NAME}; - * scheduled for removal in 6.2. This will be available both as a low and - * high cardinality key value. - */ - @Deprecated(since = "6.0.5", forRemoval = true) - CLIENT_NAME { - @Override - public String asString() { - return "client.name"; - } } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java index 365c23e58c2..48075d54bf0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java @@ -200,9 +200,6 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa else if (ex instanceof MethodValidationException subEx) { return handleMethodValidationException(subEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request); } - else if (ex instanceof BindException theEx) { - return handleBindException(theEx, headers, HttpStatus.BAD_REQUEST, request); - } else { // Unknown exception, typically a wrapper with a common MVC exception as cause // (since @ExceptionHandler type declarations also match nested causes): @@ -549,30 +546,6 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa return handleExceptionInternal(ex, body, headers, status, request); } - /** - * Customize the handling of {@link BindException}. - *

By default this method creates a {@link ProblemDetail} with the status - * and a short detail message, and then delegates to - * {@link #handleExceptionInternal}. - * @param ex the exception to handle - * @param headers the headers to use for the response - * @param status the status code to use for the response - * @param request the current request - * @return a {@code ResponseEntity} for the response to use, possibly - * {@code null} when the response is already committed - * @deprecated as of 6.0 since {@link org.springframework.web.method.annotation.ModelAttributeMethodProcessor} - * now raises the {@link MethodArgumentNotValidException} subclass instead. - */ - @Nullable - @Deprecated(since = "6.0", forRemoval = true) - protected ResponseEntity handleBindException( - BindException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { - - ProblemDetail body = ProblemDetail.forStatusAndDetail(status, "Failed to bind request"); - return handleExceptionInternal(ex, body, headers, status, request); - } - - /** * Customize the handling of {@link MethodValidationException}. *

By default this method creates a {@link ProblemDetail} with the status diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java index 2e9afabe095..d9c2528579c 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java @@ -31,8 +31,6 @@ import org.springframework.http.ProblemDetail; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.lang.Nullable; -import org.springframework.validation.BindException; -import org.springframework.validation.BindingResult; import org.springframework.validation.method.MethodValidationException; import org.springframework.web.ErrorResponse; import org.springframework.web.HttpMediaTypeNotAcceptableException; @@ -42,7 +40,6 @@ import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingPathVariableException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.ServletRequestBindingException; -import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.context.request.async.AsyncRequestTimeoutException; @@ -240,9 +237,6 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes else if (ex instanceof MethodValidationException theEx) { return handleMethodValidationException(theEx, request, response, handler); } - else if (ex instanceof BindException theEx) { - return handleBindException(theEx, request, response, handler); - } } catch (Exception handlerEx) { if (logger.isWarnEnabled()) { @@ -640,27 +634,6 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes return new ModelAndView(); } - /** - * Handle the case where an {@linkplain ModelAttribute @ModelAttribute} method - * argument has binding or validation errors and is not followed by another - * method argument of type {@link BindingResult}. - *

By default, an HTTP 400 error is sent back to the client. - * @param request current HTTP request - * @param response current HTTP response - * @param handler the executed handler - * @return an empty {@code ModelAndView} indicating the exception was handled - * @throws IOException potentially thrown from {@link HttpServletResponse#sendError} - * @deprecated as of 6.0 since {@link org.springframework.web.method.annotation.ModelAttributeMethodProcessor} - * now raises the {@link MethodArgumentNotValidException} subclass instead. - */ - @Deprecated(since = "6.0", forRemoval = true) - protected ModelAndView handleBindException(BindException ex, HttpServletRequest request, - HttpServletResponse response, @Nullable Object handler) throws IOException { - - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return new ModelAndView(); - } - /** * Invoked to send a server error. Sets the status to 500 and also sets the * request attribute "jakarta.servlet.error.exception" to the Exception. diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java index fa743061910..a2967fe3197 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java @@ -41,7 +41,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.stereotype.Controller; -import org.springframework.validation.BindException; import org.springframework.validation.MapBindingResult; import org.springframework.validation.method.MethodValidationException; import org.springframework.validation.method.MethodValidationResult; @@ -292,11 +291,6 @@ class ResponseEntityExceptionHandlerTests { testException(new MissingServletRequestPartException("partName")); } - @Test - void bindException() { - testException(new BindException(new Object(), "name")); - } - @Test void noHandlerFoundException() { HttpHeaders requestHeaders = new HttpHeaders(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java index 8ea32e43d86..36e335deb8d 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java @@ -36,7 +36,6 @@ import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.lang.Nullable; import org.springframework.validation.BeanPropertyBindingResult; -import org.springframework.validation.BindException; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -196,15 +195,6 @@ class DefaultHandlerExceptionResolverTests { assertThat(response.getErrorMessage()).contains("not present"); } - @Test - void handleBindException() { - BindException ex = new BindException(new Object(), "name"); - ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex); - assertThat(mav).as("No ModelAndView returned").isNotNull(); - assertThat(mav.isEmpty()).as("No Empty ModelAndView returned").isTrue(); - assertThat(response.getStatus()).as("Invalid status code").isEqualTo(400); - } - @Test void handleNoHandlerFoundException() { ServletServerHttpRequest req = new ServletServerHttpRequest(