Refine null-safety in spring-web and spring-websocket

See gh-32475
This commit is contained in:
Sébastien Deleuze
2024-03-25 11:02:44 +01:00
parent c4b615052e
commit a63ebe7e9d
59 changed files with 106 additions and 23 deletions

View File

@@ -211,6 +211,7 @@ final class DefaultErrorResponseBuilder implements ErrorResponse.Builder {
}
@Override
@Nullable
public Object[] getDetailMessageArguments() {
return this.detailMessageArguments;
}

View File

@@ -165,6 +165,7 @@ public class ErrorResponseException extends NestedRuntimeException implements Er
}
@Override
@Nullable
public Object[] getDetailMessageArguments() {
return this.messageDetailArguments;
}

View File

@@ -73,7 +73,7 @@ public abstract class HttpMediaTypeException extends ServletException implements
* resolving the problem "detail" through a {@code MessageSource}
* @since 6.0
*/
protected HttpMediaTypeException(String message, List<MediaType> supportedMediaTypes,
protected HttpMediaTypeException(@Nullable String message, List<MediaType> supportedMediaTypes,
@Nullable String messageDetailCode, @Nullable Object[] messageDetailArguments) {
super(message);
@@ -102,6 +102,7 @@ public abstract class HttpMediaTypeException extends ServletException implements
}
@Override
@Nullable
public Object[] getDetailMessageArguments() {
return this.messageDetailArguments;
}

View File

@@ -63,7 +63,7 @@ public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException {
* @param mediaTypes list of supported media types
* @since 6.0.5
*/
public HttpMediaTypeNotSupportedException(String message, List<MediaType> mediaTypes) {
public HttpMediaTypeNotSupportedException(@Nullable String message, List<MediaType> mediaTypes) {
super(message, mediaTypes, PARSE_ERROR_DETAIL_CODE, null);
this.contentType = null;
this.httpMethod = null;

View File

@@ -99,6 +99,7 @@ public class ServletPathExtensionContentNegotiationStrategy extends PathExtensio
* @since 4.3
*/
@Override
@Nullable
public MediaType getMediaTypeForResource(Resource resource) {
MediaType mediaType = null;
String mimeType = this.servletContext.getMimeType(resource.getFilename());

View File

@@ -50,7 +50,7 @@ public class ServletRequestBindingException extends ServletException implements
* Constructor with a message only.
* @param msg the detail message
*/
public ServletRequestBindingException(String msg) {
public ServletRequestBindingException(@Nullable String msg) {
this(msg, null, null);
}
@@ -59,7 +59,7 @@ public class ServletRequestBindingException extends ServletException implements
* @param msg the detail message
* @param cause the root cause
*/
public ServletRequestBindingException(String msg, Throwable cause) {
public ServletRequestBindingException(@Nullable String msg, @Nullable Throwable cause) {
this(msg, cause, null, null);
}
@@ -73,7 +73,7 @@ public class ServletRequestBindingException extends ServletException implements
* @since 6.0
*/
protected ServletRequestBindingException(
String msg, @Nullable String messageDetailCode, @Nullable Object[] messageDetailArguments) {
@Nullable String msg, @Nullable String messageDetailCode, @Nullable Object[] messageDetailArguments) {
this(msg, null, messageDetailCode, messageDetailArguments);
}
@@ -88,7 +88,7 @@ public class ServletRequestBindingException extends ServletException implements
* resolving the problem "detail" through a {@code MessageSource}
* @since 6.0
*/
protected ServletRequestBindingException(String msg, @Nullable Throwable cause,
protected ServletRequestBindingException(@Nullable String msg, @Nullable Throwable cause,
@Nullable String messageDetailCode, @Nullable Object[] messageDetailArguments) {
super(msg, cause);
@@ -118,6 +118,7 @@ public class ServletRequestBindingException extends ServletException implements
}
@Override
@Nullable
public Object[] getDetailMessageArguments() {
return this.messageDetailArguments;
}

View File

@@ -17,6 +17,7 @@
package org.springframework.web.bind.support;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.annotation.BindParam;
@@ -32,6 +33,7 @@ import org.springframework.web.bind.annotation.BindParam;
public final class BindParamNameResolver implements DataBinder.NameResolver {
@Override
@Nullable
public String resolveName(MethodParameter parameter) {
BindParam bindParam = parameter.getParameterAnnotation(BindParam.class);
if (bindParam != null) {

View File

@@ -162,6 +162,7 @@ public class WebExchangeDataBinder extends WebDataBinder {
private record MapValueResolver(Map<String, Object> map) implements ValueResolver {
@Override
@Nullable
public Object resolveValue(String name, Class<?> type) {
return this.map.get(name);
}

View File

@@ -84,6 +84,7 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
@Override
@Nullable
@SuppressWarnings({"rawtypes", "unchecked", "resource"})
public T extractData(ClientHttpResponse response) throws IOException {
IntrospectingClientHttpResponse responseWrapper = new IntrospectingClientHttpResponse(response);

View File

@@ -18,6 +18,8 @@ package org.springframework.web.client;
import java.io.IOException;
import org.springframework.lang.Nullable;
/**
* Exception thrown when an I/O error occurs.
*
@@ -42,7 +44,7 @@ public class ResourceAccessException extends RestClientException {
* @param msg the message
* @param ex the {@code IOException}
*/
public ResourceAccessException(String msg, IOException ex) {
public ResourceAccessException(String msg, @Nullable IOException ex) {
super(msg, ex);
}

View File

@@ -18,6 +18,7 @@ package org.springframework.web.client;
import org.springframework.core.NestedRuntimeException;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
/**
* Base class for exceptions thrown by {@link RestTemplate} in case a request
@@ -47,7 +48,7 @@ public class RestClientException extends NestedRuntimeException {
* @param msg the message
* @param ex the exception
*/
public RestClientException(String msg, Throwable ex) {
public RestClientException(String msg, @Nullable Throwable ex) {
super(msg, ex);
}

View File

@@ -25,6 +25,7 @@ import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.client.RestClient;
import org.springframework.web.service.invoker.HttpExchangeAdapter;
@@ -69,6 +70,7 @@ public final class RestClientAdapter implements HttpExchangeAdapter {
}
@Override
@Nullable
public <T> T exchangeForBody(HttpRequestValues values, ParameterizedTypeReference<T> bodyType) {
return newRequest(values).retrieve().body(bodyType);
}

View File

@@ -24,6 +24,7 @@ import jakarta.faces.context.FacesContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@@ -102,6 +103,7 @@ public class FacesRequestAttributes implements RequestAttributes {
@Override
@Nullable
public Object getAttribute(String name, int scope) {
return getAttributeMap(scope).get(name);
}
@@ -130,6 +132,7 @@ public class FacesRequestAttributes implements RequestAttributes {
}
@Override
@Nullable
public Object resolveReference(String key) {
return switch (key) {
case REFERENCE_REQUEST -> getExternalContext().getRequest();

View File

@@ -58,6 +58,7 @@ public class FacesWebRequest extends FacesRequestAttributes implements NativeWeb
}
@Override
@Nullable
@SuppressWarnings("unchecked")
public <T> T getNativeRequest(@Nullable Class<T> requiredType) {
if (requiredType != null) {
@@ -70,6 +71,7 @@ public class FacesWebRequest extends FacesRequestAttributes implements NativeWeb
}
@Override
@Nullable
@SuppressWarnings("unchecked")
public <T> T getNativeResponse(@Nullable Class<T> requiredType) {
if (requiredType != null) {

View File

@@ -143,6 +143,7 @@ public class ServletRequestAttributes extends AbstractRequestAttributes {
@Override
@Nullable
public Object getAttribute(String name, int scope) {
if (scope == SCOPE_REQUEST) {
if (!isRequestActive()) {
@@ -242,6 +243,7 @@ public class ServletRequestAttributes extends AbstractRequestAttributes {
}
@Override
@Nullable
public Object resolveReference(String key) {
if (REFERENCE_REQUEST.equals(key)) {
return this.request;

View File

@@ -98,16 +98,19 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
}
@Override
@Nullable
public Object getNativeResponse() {
return getResponse();
}
@Override
@Nullable
public <T> T getNativeRequest(@Nullable Class<T> requiredType) {
return WebUtils.getNativeRequest(getRequest(), requiredType);
}
@Override
@Nullable
public <T> T getNativeResponse(@Nullable Class<T> requiredType) {
HttpServletResponse response = getResponse();
return (response != null ? WebUtils.getNativeResponse(response, requiredType) : null);

View File

@@ -101,7 +101,7 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
* container processing thread has exited.
*/
@Override
public void setTimeout(Long timeout) {
public void setTimeout(@Nullable Long timeout) {
Assert.state(!isAsyncStarted(), "Cannot change the timeout with concurrent handling in progress");
this.timeout = timeout;
}

View File

@@ -77,6 +77,7 @@ public class ServletContextAttributeFactoryBean implements FactoryBean<Object>,
}
@Override
@Nullable
public Class<?> getObjectType() {
return (this.attribute != null ? this.attribute.getClass() : null);
}

View File

@@ -139,6 +139,7 @@ public class StaticWebApplicationContext extends StaticApplicationContext
}
@Override
@Nullable
public String[] getConfigLocations() {
return null;
}

View File

@@ -155,7 +155,7 @@ public class DefaultCorsProcessor implements CorsProcessor {
responseHeaders.setAccessControlAllowMethods(allowMethods);
}
if (preFlightRequest && !allowHeaders.isEmpty()) {
if (preFlightRequest && !CollectionUtils.isEmpty(allowHeaders)) {
responseHeaders.setAccessControlAllowHeaders(allowHeaders);
}

View File

@@ -153,7 +153,7 @@ public class DefaultCorsProcessor implements CorsProcessor {
responseHeaders.setAccessControlAllowMethods(allowMethods);
}
if (preFlightRequest && !allowHeaders.isEmpty()) {
if (preFlightRequest && !CollectionUtils.isEmpty(allowHeaders)) {
responseHeaders.setAccessControlAllowHeaders(allowHeaders);
}

View File

@@ -340,6 +340,7 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter {
@SuppressWarnings("DataFlowIssue")
@Override
@Nullable
public Object getAttribute(String name) {
if (name.equals(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE)) {
return this.forwardedPrefixExtractor.getErrorRequestUri();

View File

@@ -139,6 +139,7 @@ public class ServerHttpObservationFilter extends OncePerRequestFilter {
return observation;
}
@Nullable
private Throwable unwrapServletException(Throwable ex) {
return (ex instanceof ServletException) ? ex.getCause() : ex;
}

View File

@@ -174,6 +174,7 @@ public class ExceptionHandlerMethodResolver {
* Return the {@link Method} mapped to the given exception type, or
* {@link #NO_MATCHING_EXCEPTION_HANDLER_METHOD} if none.
*/
@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<>();
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {

View File

@@ -37,7 +37,7 @@ public class MethodArgumentConversionNotSupportedException extends ConversionNot
public MethodArgumentConversionNotSupportedException(@Nullable Object value,
@Nullable Class<?> requiredType, String name, MethodParameter param, Throwable cause) {
@Nullable Class<?> requiredType, String name, MethodParameter param, @Nullable Throwable cause) {
super(value, requiredType, cause);
this.name = name;

View File

@@ -37,7 +37,7 @@ public class MethodArgumentTypeMismatchException extends TypeMismatchException {
public MethodArgumentTypeMismatchException(@Nullable Object value,
@Nullable Class<?> requiredType, String name, MethodParameter param, Throwable cause) {
@Nullable Class<?> requiredType, String name, MethodParameter param, @Nullable Throwable cause) {
super(value, requiredType, cause);
this.name = name;

View File

@@ -65,6 +65,7 @@ class MultipartFileResource extends AbstractResource {
}
@Override
@Nullable
public String getFilename() {
return this.multipartFile.getOriginalFilename();
}

View File

@@ -130,6 +130,7 @@ public class DefaultMultipartHttpServletRequest extends AbstractMultipartHttpSer
}
@Override
@Nullable
public String getMultipartContentType(String paramOrFileName) {
MultipartFile file = getFile(paramOrFileName);
if (file != null) {
@@ -141,6 +142,7 @@ public class DefaultMultipartHttpServletRequest extends AbstractMultipartHttpSer
}
@Override
@Nullable
public HttpHeaders getMultipartHeaders(String paramOrFileName) {
String contentType = getMultipartContentType(paramOrFileName);
if (contentType != null) {
@@ -167,6 +169,7 @@ public class DefaultMultipartHttpServletRequest extends AbstractMultipartHttpSer
* lazily initializing it if necessary.
* @see #initializeMultipart()
*/
@SuppressWarnings("NullAway")
protected Map<String, String[]> getMultipartParameters() {
if (this.multipartParameters == null) {
initializeMultipart();
@@ -187,6 +190,7 @@ public class DefaultMultipartHttpServletRequest extends AbstractMultipartHttpSer
* lazily initializing it if necessary.
* @see #initializeMultipart()
*/
@SuppressWarnings("NullAway")
protected Map<String, String> getMultipartParameterContentTypes() {
if (this.multipartParameterContentTypes == null) {
initializeMultipart();

View File

@@ -176,6 +176,7 @@ public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpSe
}
@Override
@Nullable
public String getMultipartContentType(String paramOrFileName) {
try {
Part part = getPart(paramOrFileName);
@@ -187,6 +188,7 @@ public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpSe
}
@Override
@Nullable
public HttpHeaders getMultipartHeaders(String paramOrFileName) {
try {
Part part = getPart(paramOrFileName);

View File

@@ -94,6 +94,7 @@ public class ServerWebExchangeDecorator implements ServerWebExchange {
}
@Override
@Nullable
public ApplicationContext getApplicationContext() {
return getDelegate().getApplicationContext();
}

View File

@@ -18,6 +18,7 @@ package org.springframework.web.service.invoker;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.CookieValue;
/**
@@ -56,6 +57,7 @@ public class CookieValueArgumentResolver extends AbstractNamedValueArgumentResol
@Override
@Nullable
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
CookieValue annot = parameter.getParameterAnnotation(CookieValue.class);
return (annot == null ? null :

View File

@@ -408,7 +408,7 @@ public class HttpRequestValues {
/**
* Set the request body as an Object to be serialized.
*/
public void setBodyValue(Object bodyValue) {
public void setBodyValue(@Nullable Object bodyValue) {
this.bodyValue = bodyValue;
}

View File

@@ -294,6 +294,7 @@ public final class HttpServiceProxyFactory {
}
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
HttpServiceMethod httpServiceMethod = this.httpServiceMethods.get(method);

View File

@@ -18,6 +18,7 @@ package org.springframework.web.service.invoker;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.PathVariable;
/**
@@ -48,6 +49,7 @@ public class PathVariableArgumentResolver extends AbstractNamedValueArgumentReso
@Override
@Nullable
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
PathVariable annot = parameter.getParameterAnnotation(PathVariable.class);
return (annot == null ? null :

View File

@@ -221,7 +221,7 @@ public final class ReactiveHttpRequestValues extends HttpRequestValues {
* {@linkplain #setBodyPublisher(Publisher, ParameterizedTypeReference)}.
*/
@Override
public void setBodyValue(Object bodyValue) {
public void setBodyValue(@Nullable Object bodyValue) {
super.setBodyValue(bodyValue);
this.body = null;
this.bodyElementType = null;

View File

@@ -17,6 +17,7 @@
package org.springframework.web.service.invoker;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RequestAttribute;
/**
@@ -40,6 +41,7 @@ public class RequestAttributeArgumentResolver extends AbstractNamedValueArgument
@Override
@Nullable
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestAttribute annot = parameter.getParameterAnnotation(RequestAttribute.class);
return (annot == null ? null :

View File

@@ -18,6 +18,7 @@ package org.springframework.web.service.invoker;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RequestHeader;
/**
@@ -57,6 +58,7 @@ public class RequestHeaderArgumentResolver extends AbstractNamedValueArgumentRes
@Override
@Nullable
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestHeader annot = parameter.getParameterAnnotation(RequestHeader.class);
return (annot == null ? null :

View File

@@ -18,6 +18,7 @@ package org.springframework.web.service.invoker;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RequestParam;
/**
@@ -60,6 +61,7 @@ public class RequestParamArgumentResolver extends AbstractNamedValueArgumentReso
@Override
@Nullable
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestParam annot = parameter.getParameterAnnotation(RequestParam.class);
return (annot == null ? null :

View File

@@ -78,6 +78,7 @@ public class RequestPartArgumentResolver extends AbstractNamedValueArgumentResol
@Override
@Nullable
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestPart annot = parameter.getParameterAnnotation(RequestPart.class);
boolean isMultiPartFile = parameter.nestedIfOptional().getNestedParameterType().equals(MultipartFile.class);

View File

@@ -1077,6 +1077,7 @@ final class HierarchicalUriComponents extends UriComponents {
}
@Override
@Nullable
public Object getValue(@Nullable String name) {
Object value = this.delegate.getValue(name);
if (ObjectUtils.isArray(value)) {

View File

@@ -676,7 +676,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
}
@Override
public UriComponentsBuilder queryParam(String name, Object... values) {
public UriComponentsBuilder queryParam(String name, @Nullable Object... values) {
Assert.notNull(name, "Name must not be null");
if (!ObjectUtils.isEmpty(values)) {
for (Object value : values) {
@@ -926,6 +926,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
}
@Override
@Nullable
public PathComponent build() {
if (this.path.length() == 0) {
return null;
@@ -976,6 +977,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
}
@Override
@Nullable
public PathComponent build() {
return (this.pathSegments.isEmpty() ? null :
new HierarchicalUriComponents.PathSegmentComponent(this.pathSegments));

View File

@@ -540,7 +540,7 @@ public abstract class WebUtils {
* @param name the name of the attribute
* @param value the suggested value of the attribute
*/
private static void exposeRequestAttributeIfNotPresent(ServletRequest request, String name, Object value) {
private static void exposeRequestAttributeIfNotPresent(ServletRequest request, String name, @Nullable Object value) {
if (request.getAttribute(name) == null) {
request.setAttribute(name, value);
}