diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/AbstractWebFluxEndpointHandlerMapping.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/AbstractWebFluxEndpointHandlerMapping.java index 0667a3d0704..683d181817f 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/AbstractWebFluxEndpointHandlerMapping.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/AbstractWebFluxEndpointHandlerMapping.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -56,11 +57,13 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.http.ResponseEntity.BodyBuilder; import org.springframework.security.authorization.AuthorityAuthorizationManager; import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.util.AntPathMatcher; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; @@ -96,13 +99,11 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi private final EndpointMediaTypes endpointMediaTypes; - private final CorsConfiguration corsConfiguration; + private final @Nullable CorsConfiguration corsConfiguration; - private final Method handleWriteMethod = ReflectionUtils.findMethod(WriteOperationHandler.class, "handle", - ServerWebExchange.class, Map.class); + private final Method handleWriteMethod = getHandleWriteMethod(); - private final Method handleReadMethod = ReflectionUtils.findMethod(ReadOperationHandler.class, "handle", - ServerWebExchange.class); + private final Method handleReadMethod = getHandleReadMethod(); private final boolean shouldRegisterLinksMapping; @@ -117,7 +118,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi */ public AbstractWebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, Collection endpoints, EndpointMediaTypes endpointMediaTypes, - CorsConfiguration corsConfiguration, boolean shouldRegisterLinksMapping) { + @Nullable CorsConfiguration corsConfiguration, boolean shouldRegisterLinksMapping) { this.endpointMapping = endpointMapping; this.endpoints = endpoints; this.endpointMediaTypes = endpointMediaTypes; @@ -126,6 +127,19 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi setOrder(-100); } + private static Method getHandleWriteMethod() { + Method method = ReflectionUtils.findMethod(WriteOperationHandler.class, "handle", ServerWebExchange.class, + Map.class); + Assert.state(method != null, "'method' must not be null"); + return method; + } + + private static Method getHandleReadMethod() { + Method method = ReflectionUtils.findMethod(ReadOperationHandler.class, "handle", ServerWebExchange.class); + Assert.state(method != null, "'method' must not be null"); + return method; + } + @Override protected void initHandlerMethods() { for (ExposableWebEndpoint endpoint : this.endpoints) { @@ -204,8 +218,9 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi .produces(produces) .build(); LinksHandler linksHandler = getLinksHandler(); - registerMapping(mapping, linksHandler, - ReflectionUtils.findMethod(linksHandler.getClass(), "links", ServerWebExchange.class)); + Method linksMethod = ReflectionUtils.findMethod(linksHandler.getClass(), "links", ServerWebExchange.class); + Assert.state(linksMethod != null, "'linksMethod' must not be null"); + registerMapping(mapping, linksHandler, linksMethod); } @Override @@ -214,12 +229,13 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi } @Override - protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mapping) { + protected @Nullable CorsConfiguration initCorsConfiguration(Object handler, Method method, + RequestMappingInfo mapping) { return this.corsConfiguration; } @Override - protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) { + protected @Nullable CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) { CorsConfiguration corsConfiguration = super.getCorsConfiguration(handler, exchange); return (corsConfiguration != null) ? corsConfiguration : this.corsConfiguration; } @@ -230,7 +246,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi } @Override - protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { + protected @Nullable RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { return null; } @@ -277,7 +293,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi } @Override - public Object invoke(InvocationContext context) { + public @Nullable Object invoke(InvocationContext context) { try { return this.invoker.invoke(context); } @@ -304,7 +320,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi @FunctionalInterface protected interface ReactiveWebOperation { - Mono> handle(ServerWebExchange exchange, Map body); + Mono> handle(ServerWebExchange exchange, @Nullable Map body); } @@ -355,7 +371,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi } @Override - public Mono> handle(ServerWebExchange exchange, Map body) { + public Mono> handle(ServerWebExchange exchange, @Nullable Map body) { Map arguments = getArguments(exchange, body); OperationArgumentResolver serverNamespaceArgumentResolver = OperationArgumentResolver .of(WebServerNamespace.class, () -> WebServerNamespace @@ -369,7 +385,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi exchange.getRequest().getMethod())); } - private Map getArguments(ServerWebExchange exchange, Map body) { + private Map getArguments(ServerWebExchange exchange, @Nullable Map body) { Map arguments = new LinkedHashMap<>(getTemplateVariables(exchange)); String matchAllRemainingPathSegmentsVariable = this.operation.getRequestPredicate() .getMatchAllRemainingPathSegmentsVariable(); @@ -387,6 +403,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi private Object getRemainingPathSegments(ServerWebExchange exchange) { PathPattern pathPattern = exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + Assert.state(pathPattern != null, "'pathPattern' must not be null"); if (pathPattern.hasPatternSyntax()) { String remainingSegments = pathPattern .extractPathWithinPattern(exchange.getRequest().getPath().pathWithinApplication()) @@ -407,10 +424,12 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi } private Map getTemplateVariables(ServerWebExchange exchange) { - return exchange.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + Map result = exchange.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + Assert.state(result != null, "'result' must not be null"); + return result; } - private Mono> handleResult(Publisher result, HttpMethod httpMethod) { + private Mono> handleResult(@Nullable Publisher result, HttpMethod httpMethod) { if (result instanceof Flux) { result = ((Flux) result).collectList(); } @@ -428,9 +447,11 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi } MediaType contentType = (webEndpointResponse.getContentType() != null) ? new MediaType(webEndpointResponse.getContentType()) : null; - return ResponseEntity.status(webEndpointResponse.getStatus()) - .contentType(contentType) - .body(webEndpointResponse.getBody()); + BodyBuilder builder = ResponseEntity.status(webEndpointResponse.getStatus()); + if (contentType != null) { + builder = builder.contentType(contentType); + } + return builder.body(webEndpointResponse.getBody()); } @Override @@ -512,18 +533,18 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi private static final String ROLE_PREFIX = "ROLE_"; - private final Authentication authentication; + private final @Nullable Authentication authentication; - ReactiveSecurityContext(Authentication authentication) { + ReactiveSecurityContext(@Nullable Authentication authentication) { this.authentication = authentication; } - private Authentication getAuthentication() { + private @Nullable Authentication getAuthentication() { return this.authentication; } @Override - public Principal getPrincipal() { + public @Nullable Principal getPrincipal() { return this.authentication; } @@ -531,7 +552,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi public boolean isUserInRole(String role) { String authority = (!role.startsWith(ROLE_PREFIX)) ? ROLE_PREFIX + role : role; AuthorizationResult result = AuthorityAuthorizationManager.hasAuthority(authority) - .authorize(this::getAuthentication, null); + .authorize(this::getAuthentication, new Object()); return result != null && result.isGranted(); } @@ -542,7 +563,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi private final ReflectiveRuntimeHintsRegistrar reflectiveRegistrar = new ReflectiveRuntimeHintsRegistrar(); @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { this.reflectiveRegistrar.registerRuntimeHints(hints, WriteOperationHandler.class, ReadOperationHandler.class); } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/AdditionalHealthEndpointPathsWebFluxHandlerMapping.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/AdditionalHealthEndpointPathsWebFluxHandlerMapping.java index b0c743f01c1..b9627fc28ed 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/AdditionalHealthEndpointPathsWebFluxHandlerMapping.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/AdditionalHealthEndpointPathsWebFluxHandlerMapping.java @@ -20,7 +20,11 @@ import java.util.Collection; import java.util.Collections; import java.util.Set; +import org.jspecify.annotations.Nullable; +import reactor.core.publisher.Mono; + import org.springframework.boot.actuate.endpoint.web.EndpointMapping; +import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint; import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.WebOperationRequestPredicate; @@ -42,19 +46,19 @@ public class AdditionalHealthEndpointPathsWebFluxHandlerMapping extends Abstract private final EndpointMapping endpointMapping; - private final ExposableWebEndpoint healthEndpoint; + private final @Nullable ExposableWebEndpoint healthEndpoint; private final Set groups; public AdditionalHealthEndpointPathsWebFluxHandlerMapping(EndpointMapping endpointMapping, - ExposableWebEndpoint healthEndpoint, Set groups) { - super(endpointMapping, asList(healthEndpoint), null, null, false); + @Nullable ExposableWebEndpoint healthEndpoint, Set groups) { + super(endpointMapping, asList(healthEndpoint), new EndpointMediaTypes(), null, false); this.endpointMapping = endpointMapping; this.groups = groups; this.healthEndpoint = healthEndpoint; } - private static Collection asList(ExposableWebEndpoint healthEndpoint) { + private static Collection asList(@Nullable ExposableWebEndpoint healthEndpoint) { return (healthEndpoint != null) ? Collections.singletonList(healthEndpoint) : Collections.emptyList(); } @@ -90,7 +94,7 @@ public class AdditionalHealthEndpointPathsWebFluxHandlerMapping extends Abstract @Override protected LinksHandler getLinksHandler() { - return null; + return (exchange) -> Mono.empty(); } } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/ControllerEndpointHandlerMapping.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/ControllerEndpointHandlerMapping.java index 9ff4429b697..06ee324ef08 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/ControllerEndpointHandlerMapping.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/ControllerEndpointHandlerMapping.java @@ -25,6 +25,8 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.actuate.endpoint.Access; import org.springframework.boot.actuate.endpoint.EndpointAccessResolver; import org.springframework.boot.actuate.endpoint.web.EndpointMapping; @@ -58,7 +60,7 @@ public class ControllerEndpointHandlerMapping extends RequestMappingHandlerMappi private final EndpointMapping endpointMapping; - private final CorsConfiguration corsConfiguration; + private final @Nullable CorsConfiguration corsConfiguration; private final Map handlers; @@ -72,7 +74,7 @@ public class ControllerEndpointHandlerMapping extends RequestMappingHandlerMappi * @param corsConfiguration the CORS configuration for the endpoints or {@code null} */ public ControllerEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection endpoints, CorsConfiguration corsConfiguration) { + Collection endpoints, @Nullable CorsConfiguration corsConfiguration) { this(endpointMapping, endpoints, corsConfiguration, (endpointId, defaultAccess) -> Access.NONE); } @@ -85,7 +87,7 @@ public class ControllerEndpointHandlerMapping extends RequestMappingHandlerMappi * @param endpointAccessResolver resolver for endpoint access */ public ControllerEndpointHandlerMapping(EndpointMapping endpointMapping, - Collection endpoints, CorsConfiguration corsConfiguration, + Collection endpoints, @Nullable CorsConfiguration corsConfiguration, EndpointAccessResolver endpointAccessResolver) { Assert.notNull(endpointMapping, "'endpointMapping' must not be null"); Assert.notNull(endpoints, "'endpoints' must not be null"); @@ -110,6 +112,7 @@ public class ControllerEndpointHandlerMapping extends RequestMappingHandlerMappi @Override protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { ExposableControllerEndpoint endpoint = this.handlers.get(handler); + Assert.state(endpoint != null, "'endpoint' must not be null"); Access access = this.accessResolver.accessFor(endpoint.getEndpointId(), endpoint.getDefaultAccess()); if (access == Access.NONE) { return; @@ -157,7 +160,8 @@ public class ControllerEndpointHandlerMapping extends RequestMappingHandlerMappi } @Override - protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mapping) { + protected @Nullable CorsConfiguration initCorsConfiguration(Object handler, Method method, + RequestMappingInfo mapping) { return this.corsConfiguration; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/WebFluxEndpointHandlerMapping.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/WebFluxEndpointHandlerMapping.java index acdf3ea60a8..f443b5cadda 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/WebFluxEndpointHandlerMapping.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/WebFluxEndpointHandlerMapping.java @@ -20,6 +20,8 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.aot.hint.BindingReflectionHintsRegistrar; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; @@ -65,7 +67,7 @@ public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandle * @param shouldRegisterLinksMapping whether the links endpoint should be registered */ public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, Collection endpoints, - EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration, + EndpointMediaTypes endpointMediaTypes, @Nullable CorsConfiguration corsConfiguration, EndpointLinksResolver linksResolver, boolean shouldRegisterLinksMapping) { super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, shouldRegisterLinksMapping); this.linksResolver = linksResolver; @@ -107,7 +109,7 @@ public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandle private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar(); @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { this.reflectiveRegistrar.registerRuntimeHints(hints, WebFluxLinksHandler.class); this.bindingRegistrar.registerReflectionHints(hints.reflection(), Link.class); } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/package-info.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/package-info.java index 800e879b191..308c71af4c2 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/package-info.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/endpoint/web/package-info.java @@ -17,4 +17,7 @@ /** * Spring WebFlux support for actuator endpoints. */ +@NullMarked package org.springframework.boot.webflux.actuate.endpoint.web; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/HttpExchangesWebFilter.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/HttpExchangesWebFilter.java index 1e402bc1acf..e187244aa92 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/HttpExchangesWebFilter.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/HttpExchangesWebFilter.java @@ -19,6 +19,7 @@ package org.springframework.boot.webflux.actuate.exchanges; import java.security.Principal; import java.util.Set; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.boot.actuate.web.exchanges.HttpExchange; @@ -98,20 +99,20 @@ public class HttpExchangesWebFilter implements WebFilter, Ordered { */ private static class PrincipalAndSession { - private final Principal principal; + private final @Nullable Principal principal; - private final WebSession session; + private final @Nullable WebSession session; PrincipalAndSession(Object[] zipped) { this.principal = (zipped[0] != NONE) ? (Principal) zipped[0] : null; this.session = (zipped[1] != NONE) ? (WebSession) zipped[1] : null; } - Principal getPrincipal() { + @Nullable Principal getPrincipal() { return this.principal; } - String getSessionId() { + @Nullable String getSessionId() { return (this.session != null && this.session.isStarted()) ? this.session.getId() : null; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/RecordableServerHttpRequest.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/RecordableServerHttpRequest.java index 63bdebff43d..40dc45dc162 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/RecordableServerHttpRequest.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/RecordableServerHttpRequest.java @@ -24,6 +24,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.actuate.web.exchanges.RecordableHttpRequest; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -41,7 +43,7 @@ class RecordableServerHttpRequest implements RecordableHttpRequest { private final URI uri; - private final String remoteAddress; + private final @Nullable String remoteAddress; RecordableServerHttpRequest(ServerHttpRequest request) { this.method = request.getMethod().name(); @@ -50,7 +52,7 @@ class RecordableServerHttpRequest implements RecordableHttpRequest { this.remoteAddress = getRemoteAddress(request); } - private static String getRemoteAddress(ServerHttpRequest request) { + private static @Nullable String getRemoteAddress(ServerHttpRequest request) { InetSocketAddress remoteAddress = request.getRemoteAddress(); InetAddress address = (remoteAddress != null) ? remoteAddress.getAddress() : null; return (address != null) ? address.toString() : null; @@ -74,7 +76,7 @@ class RecordableServerHttpRequest implements RecordableHttpRequest { } @Override - public String getRemoteAddress() { + public @Nullable String getRemoteAddress() { return this.remoteAddress; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/package-info.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/package-info.java index 3175301f3b5..5e6bdf15cf9 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/package-info.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/exchanges/package-info.java @@ -19,4 +19,7 @@ * * @see org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository */ +@NullMarked package org.springframework.boot.webflux.actuate.exchanges; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlerMappingDescription.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlerMappingDescription.java index 4b7ba4acedb..fbb188edf32 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlerMappingDescription.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlerMappingDescription.java @@ -16,6 +16,8 @@ package org.springframework.boot.webflux.actuate.mappings; +import org.jspecify.annotations.Nullable; + import org.springframework.web.reactive.DispatcherHandler; /** @@ -30,9 +32,10 @@ public class DispatcherHandlerMappingDescription { private final String handler; - private final DispatcherHandlerMappingDetails details; + private final @Nullable DispatcherHandlerMappingDetails details; - DispatcherHandlerMappingDescription(String predicate, String handler, DispatcherHandlerMappingDetails details) { + DispatcherHandlerMappingDescription(String predicate, String handler, + @Nullable DispatcherHandlerMappingDetails details) { this.predicate = predicate; this.handler = handler; this.details = details; @@ -46,7 +49,7 @@ public class DispatcherHandlerMappingDescription { return this.predicate; } - public DispatcherHandlerMappingDetails getDetails() { + public @Nullable DispatcherHandlerMappingDetails getDetails() { return this.details; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlerMappingDetails.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlerMappingDetails.java index 9ea925e815c..3a13e675bcc 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlerMappingDetails.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlerMappingDetails.java @@ -16,6 +16,8 @@ package org.springframework.boot.webflux.actuate.mappings; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.actuate.web.mappings.HandlerMethodDescription; import org.springframework.web.reactive.DispatcherHandler; @@ -27,33 +29,33 @@ import org.springframework.web.reactive.DispatcherHandler; */ public class DispatcherHandlerMappingDetails { - private HandlerMethodDescription handlerMethod; + private @Nullable HandlerMethodDescription handlerMethod; - private HandlerFunctionDescription handlerFunction; + private @Nullable HandlerFunctionDescription handlerFunction; - private RequestMappingConditionsDescription requestMappingConditions; + private @Nullable RequestMappingConditionsDescription requestMappingConditions; - public HandlerMethodDescription getHandlerMethod() { + public @Nullable HandlerMethodDescription getHandlerMethod() { return this.handlerMethod; } - void setHandlerMethod(HandlerMethodDescription handlerMethod) { + void setHandlerMethod(@Nullable HandlerMethodDescription handlerMethod) { this.handlerMethod = handlerMethod; } - public HandlerFunctionDescription getHandlerFunction() { + public @Nullable HandlerFunctionDescription getHandlerFunction() { return this.handlerFunction; } - void setHandlerFunction(HandlerFunctionDescription handlerFunction) { + void setHandlerFunction(@Nullable HandlerFunctionDescription handlerFunction) { this.handlerFunction = handlerFunction; } - public RequestMappingConditionsDescription getRequestMappingConditions() { + public @Nullable RequestMappingConditionsDescription getRequestMappingConditions() { return this.requestMappingConditions; } - void setRequestMappingConditions(RequestMappingConditionsDescription requestMappingConditions) { + void setRequestMappingConditions(@Nullable RequestMappingConditionsDescription requestMappingConditions) { this.requestMappingConditions = requestMappingConditions; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlersMappingDescriptionProvider.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlersMappingDescriptionProvider.java index 72b22f97c13..055c03b5603 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlersMappingDescriptionProvider.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/DispatcherHandlersMappingDescriptionProvider.java @@ -18,6 +18,7 @@ package org.springframework.boot.webflux.actuate.mappings; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -25,6 +26,7 @@ import java.util.Map.Entry; import java.util.function.Function; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.aot.hint.BindingReflectionHintsRegistrar; @@ -78,7 +80,11 @@ public class DispatcherHandlersMappingDescriptionProvider implements MappingDesc } private List describeMappings(DispatcherHandler dispatcherHandler) { - return dispatcherHandler.getHandlerMappings().stream().flatMap(this::describe).toList(); + List handlerMappings = dispatcherHandler.getHandlerMappings(); + if (handlerMappings == null) { + return Collections.emptyList(); + } + return handlerMappings.stream().flatMap(this::describe).toList(); } @SuppressWarnings("unchecked") @@ -202,7 +208,7 @@ public class DispatcherHandlersMappingDescriptionProvider implements MappingDesc private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar(); @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { this.bindingRegistrar.registerReflectionHints(hints.reflection(), DispatcherHandlerMappingDescription.class); } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/RequestMappingConditionsDescription.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/RequestMappingConditionsDescription.java index 0446896bfd3..c368e96eb12 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/RequestMappingConditionsDescription.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/RequestMappingConditionsDescription.java @@ -21,6 +21,8 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; + import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.reactive.result.condition.MediaTypeExpression; import org.springframework.web.reactive.result.condition.NameValueExpression; @@ -131,7 +133,7 @@ public class RequestMappingConditionsDescription { private final String name; - private final Object value; + private final @Nullable Object value; private final boolean negated; @@ -145,7 +147,7 @@ public class RequestMappingConditionsDescription { return this.name; } - public Object getValue() { + public @Nullable Object getValue() { return this.value; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/package-info.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/package-info.java index 69daa3cbc8e..dc1edca9b94 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/package-info.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/actuate/mappings/package-info.java @@ -17,4 +17,7 @@ /** * Actuator reactive request mappings support. */ +@NullMarked package org.springframework.boot.webflux.actuate.mappings; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/ReactiveMultipartProperties.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/ReactiveMultipartProperties.java index 57b90b83df7..ef3372f4a1a 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/ReactiveMultipartProperties.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/ReactiveMultipartProperties.java @@ -19,6 +19,8 @@ package org.springframework.boot.webflux.autoconfigure; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; import org.springframework.http.codec.multipart.PartEventHttpMessageReader; @@ -64,7 +66,7 @@ public class ReactiveMultipartProperties { * directory named 'spring-multipart' created under the system temporary directory. * Ignored when using the PartEvent streaming support. */ - private String fileStorageDirectory; + private @Nullable String fileStorageDirectory; /** * Character set used to decode headers. @@ -103,11 +105,11 @@ public class ReactiveMultipartProperties { this.maxParts = maxParts; } - public String getFileStorageDirectory() { + public @Nullable String getFileStorageDirectory() { return this.fileStorageDirectory; } - public void setFileStorageDirectory(String fileStorageDirectory) { + public void setFileStorageDirectory(@Nullable String fileStorageDirectory) { this.fileStorageDirectory = fileStorageDirectory; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/ResourceChainResourceHandlerRegistrationCustomizer.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/ResourceChainResourceHandlerRegistrationCustomizer.java index 397f0f654ca..d9671c7ba36 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/ResourceChainResourceHandlerRegistrationCustomizer.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/ResourceChainResourceHandlerRegistrationCustomizer.java @@ -17,6 +17,7 @@ package org.springframework.boot.webflux.autoconfigure; import org.springframework.boot.autoconfigure.web.WebProperties.Resources; +import org.springframework.util.Assert; import org.springframework.web.reactive.config.ResourceChainRegistration; import org.springframework.web.reactive.config.ResourceHandlerRegistration; import org.springframework.web.reactive.resource.EncodedResourceResolver; @@ -58,6 +59,7 @@ class ResourceChainResourceHandlerRegistrationCustomizer implements ResourceHand if (properties.getFixed().isEnabled()) { String version = properties.getFixed().getVersion(); String[] paths = properties.getFixed().getPaths(); + Assert.state(version != null, "'version' must not be null"); resolver.addFixedVersionStrategy(version, paths); } if (properties.getContent().isEnabled()) { diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxAutoConfiguration.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxAutoConfiguration.java index ff252ab8b91..724ed47e375 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxAutoConfiguration.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxAutoConfiguration.java @@ -18,6 +18,7 @@ package org.springframework.boot.webflux.autoconfigure; import java.time.Duration; import java.util.List; +import java.util.Locale; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -65,7 +66,9 @@ import org.springframework.core.env.Environment; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.format.FormatterRegistry; import org.springframework.format.support.FormattingConversionService; +import org.springframework.http.CacheControl; import org.springframework.http.codec.ServerCodecConfigurer; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.validation.Validator; import org.springframework.web.accept.ApiVersionParser; @@ -137,7 +140,7 @@ public final class WebFluxAutoConfiguration { static class WelcomePageConfiguration { @Bean - RouterFunctionMapping welcomePageRouterFunctionMapping(ApplicationContext applicationContext, + @Nullable RouterFunctionMapping welcomePageRouterFunctionMapping(ApplicationContext applicationContext, WebFluxProperties webFluxProperties, WebProperties webProperties) { String[] staticLocations = webProperties.getResources().getStaticLocations(); WelcomePageRouterFunctionFactory factory = new WelcomePageRouterFunctionFactory( @@ -258,7 +261,10 @@ public final class WebFluxAutoConfiguration { if (cachePeriod != null && cacheControl.getMaxAge() == null) { cacheControl.setMaxAge(cachePeriod); } - registration.setCacheControl(cacheControl.toHttpCacheControl()); + CacheControl httpCacheControl = cacheControl.toHttpCacheControl(); + if (httpCacheControl != null) { + registration.setCacheControl(httpCacheControl); + } registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified()); } @@ -310,7 +316,7 @@ public final class WebFluxAutoConfiguration { private final ServerProperties serverProperties; - private final WebFluxRegistrations webFluxRegistrations; + private final @Nullable WebFluxRegistrations webFluxRegistrations; EnableWebFluxConfiguration(WebFluxProperties webFluxProperties, WebProperties webProperties, ServerProperties serverProperties, ObjectProvider webFluxRegistrations) { @@ -340,7 +346,9 @@ public final class WebFluxAutoConfiguration { getClass().getClassLoader())) { return super.webFluxValidator(); } - return ValidatorAdapter.get(getApplicationContext(), getValidator()); + ApplicationContext applicationContext = getApplicationContext(); + Assert.state(applicationContext != null, "'applicationContext' must not be null"); + return ValidatorAdapter.get(applicationContext, getValidator()); } @Override @@ -369,11 +377,13 @@ public final class WebFluxAutoConfiguration { @Override @ConditionalOnMissingBean(name = WebHttpHandlerBuilder.LOCALE_CONTEXT_RESOLVER_BEAN_NAME) public LocaleContextResolver localeContextResolver() { + Locale locale = this.webProperties.getLocale(); if (this.webProperties.getLocaleResolver() == WebProperties.LocaleResolver.FIXED) { - return new FixedLocaleContextResolver(this.webProperties.getLocale()); + Assert.state(locale != null, "'locale' must not be null"); + return new FixedLocaleContextResolver(locale); } AcceptHeaderLocaleContextResolver localeContextResolver = new AcceptHeaderLocaleContextResolver(); - localeContextResolver.setDefaultLocale(this.webProperties.getLocale()); + localeContextResolver.setDefaultLocale(locale); return localeContextResolver; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxProperties.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxProperties.java index b3ebc915cf6..ea37ff12760 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxProperties.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxProperties.java @@ -20,6 +20,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.bind.Name; import org.springframework.http.MediaType; @@ -38,7 +40,7 @@ public class WebFluxProperties { /** * Base path for all web handlers. */ - private String basePath; + private @Nullable String basePath; private final Format format = new Format(); @@ -56,15 +58,15 @@ public class WebFluxProperties { */ private String webjarsPathPattern = "/webjars/**"; - public String getBasePath() { + public @Nullable String getBasePath() { return this.basePath; } - public void setBasePath(String basePath) { + public void setBasePath(@Nullable String basePath) { this.basePath = cleanBasePath(basePath); } - private String cleanBasePath(String basePath) { + private @Nullable String cleanBasePath(@Nullable String basePath) { String candidate = null; if (StringUtils.hasLength(basePath)) { candidate = basePath.strip(); @@ -114,41 +116,41 @@ public class WebFluxProperties { * Date format to use, for example 'dd/MM/yyyy'. Used for formatting of * java.util.Date and java.time.LocalDate. */ - private String date; + private @Nullable String date; /** * Time format to use, for example 'HH:mm:ss'. Used for formatting of java.time's * LocalTime and OffsetTime. */ - private String time; + private @Nullable String time; /** * Date-time format to use, for example 'yyyy-MM-dd HH:mm:ss'. Used for formatting * of java.time's LocalDateTime, OffsetDateTime, and ZonedDateTime. */ - private String dateTime; + private @Nullable String dateTime; - public String getDate() { + public @Nullable String getDate() { return this.date; } - public void setDate(String date) { + public void setDate(@Nullable String date) { this.date = date; } - public String getTime() { + public @Nullable String getTime() { return this.time; } - public void setTime(String time) { + public void setTime(@Nullable String time) { this.time = time; } - public String getDateTime() { + public @Nullable String getDateTime() { return this.dateTime; } - public void setDateTime(String dateTime) { + public void setDateTime(@Nullable String dateTime) { this.dateTime = dateTime; } @@ -176,58 +178,58 @@ public class WebFluxProperties { /** * Whether the API version is required with each request. */ - private Boolean required; + private @Nullable Boolean required; /** * Default version that should be used for each request. */ @Name("default") - private String defaultVersion; + private @Nullable String defaultVersion; /** * Supported versions. */ - private List supported; + private @Nullable List supported; /** * Whether supported versions should be detected from controllers. */ - private Boolean detectSupported; + private @Nullable Boolean detectSupported; /** * How version details should be inserted into requests. */ private final Use use = new Use(); - public Boolean getRequired() { + public @Nullable Boolean getRequired() { return this.required; } - public void setRequired(Boolean required) { + public void setRequired(@Nullable Boolean required) { this.required = required; } - public String getDefaultVersion() { + public @Nullable String getDefaultVersion() { return this.defaultVersion; } - public void setDefaultVersion(String defaultVersion) { + public void setDefaultVersion(@Nullable String defaultVersion) { this.defaultVersion = defaultVersion; } - public List getSupported() { + public @Nullable List getSupported() { return this.supported; } - public void setSupported(List supported) { + public void setSupported(@Nullable List supported) { this.supported = supported; } - public Boolean getDetectSupported() { + public @Nullable Boolean getDetectSupported() { return this.detectSupported; } - public void setDetectSupported(Boolean detectSupported) { + public void setDetectSupported(@Nullable Boolean detectSupported) { this.detectSupported = detectSupported; } @@ -240,44 +242,44 @@ public class WebFluxProperties { /** * Use the HTTP header with the given name to obtain the version. */ - private String header; + private @Nullable String header; /** * Use the query parameter with the given name to obtain the version. */ - private String queryParameter; + private @Nullable String queryParameter; /** * Use the path segment at the given index to obtain the version. */ - private Integer pathSegment; + private @Nullable Integer pathSegment; /** * Use the media type parameter with the given name to obtain the version. */ private Map mediaTypeParameter = new LinkedHashMap<>(); - public String getHeader() { + public @Nullable String getHeader() { return this.header; } - public void setHeader(String header) { + public void setHeader(@Nullable String header) { this.header = header; } - public String getQueryParameter() { + public @Nullable String getQueryParameter() { return this.queryParameter; } - public void setQueryParameter(String queryParameter) { + public void setQueryParameter(@Nullable String queryParameter) { this.queryParameter = queryParameter; } - public Integer getPathSegment() { + public @Nullable Integer getPathSegment() { return this.pathSegment; } - public void setPathSegment(Integer pathSegment) { + public void setPathSegment(@Nullable Integer pathSegment) { this.pathSegment = pathSegment; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxRegistrations.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxRegistrations.java index 257bb59d330..4fbc9ba1e85 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxRegistrations.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxRegistrations.java @@ -16,6 +16,8 @@ package org.springframework.boot.webflux.autoconfigure; +import org.jspecify.annotations.Nullable; + import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; @@ -38,7 +40,7 @@ public interface WebFluxRegistrations { * processed by the WebFlux configuration. * @return the custom {@link RequestMappingHandlerMapping} instance */ - default RequestMappingHandlerMapping getRequestMappingHandlerMapping() { + default @Nullable RequestMappingHandlerMapping getRequestMappingHandlerMapping() { return null; } @@ -47,7 +49,7 @@ public interface WebFluxRegistrations { * processed by the WebFlux configuration. * @return the custom {@link RequestMappingHandlerAdapter} instance */ - default RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() { + default @Nullable RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() { return null; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebSessionIdResolverAutoConfiguration.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebSessionIdResolverAutoConfiguration.java index b19428f1fa0..7078d64d94f 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebSessionIdResolverAutoConfiguration.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebSessionIdResolverAutoConfiguration.java @@ -76,6 +76,11 @@ public final class WebSessionIdResolverAutoConfiguration { map.from(cookie::getSecure).to(builder::secure); map.from(cookie::getMaxAge).to(builder::maxAge); map.from(cookie::getPartitioned).to(builder::partitioned); + setSameSite(builder, map, cookie); + } + + @SuppressWarnings("NullAway") // Lambda isn't detected with the correct nullability + private void setSameSite(ResponseCookieBuilder builder, PropertyMapper map, Cookie cookie) { map.from(cookie::getSameSite).as(SameSite::attributeValue).to(builder::sameSite); } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WelcomePageRouterFunctionFactory.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WelcomePageRouterFunctionFactory.java index 458ceeecb5e..8550f151cff 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WelcomePageRouterFunctionFactory.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WelcomePageRouterFunctionFactory.java @@ -18,6 +18,8 @@ package org.springframework.boot.webflux.autoconfigure; import java.util.Arrays; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders; import org.springframework.context.ApplicationContext; import org.springframework.core.io.Resource; @@ -41,7 +43,7 @@ final class WelcomePageRouterFunctionFactory { private final String staticPathPattern; - private final Resource welcomePage; + private final @Nullable Resource welcomePage; private final boolean welcomePageTemplateExists; @@ -52,7 +54,7 @@ final class WelcomePageRouterFunctionFactory { this.welcomePageTemplateExists = welcomeTemplateExists(templateAvailabilityProviders, applicationContext); } - private Resource getWelcomePage(ResourceLoader resourceLoader, String[] staticLocations) { + private @Nullable Resource getWelcomePage(ResourceLoader resourceLoader, String[] staticLocations) { return Arrays.stream(staticLocations) .map((location) -> getIndexHtml(resourceLoader, location)) .filter(this::isReadable) @@ -78,7 +80,7 @@ final class WelcomePageRouterFunctionFactory { return templateAvailabilityProviders.getProvider("index", applicationContext) != null; } - RouterFunction createRouterFunction() { + @Nullable RouterFunction createRouterFunction() { if (this.welcomePage != null && "/**".equals(this.staticPathPattern)) { return RouterFunctions.route(GET("/").and(accept(MediaType.TEXT_HTML)), (req) -> ServerResponse.ok().contentType(MediaType.TEXT_HTML).bodyValue(this.welcomePage)); diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/actuate/endpoint/web/package-info.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/actuate/endpoint/web/package-info.java index ffbe43f225c..2eea2abfdc2 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/actuate/endpoint/web/package-info.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/actuate/endpoint/web/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for Spring WebFlux actuator web endpoint support. */ +@NullMarked package org.springframework.boot.webflux.autoconfigure.actuate.endpoint.web; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/actuate/web/package-info.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/actuate/web/package-info.java index 80a78d66341..fafaf7b3274 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/actuate/web/package-info.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/actuate/web/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for WebFlux-based actuator web concerns. */ +@NullMarked package org.springframework.boot.webflux.autoconfigure.actuate.web; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/AbstractErrorWebExceptionHandler.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/AbstractErrorWebExceptionHandler.java index 27c013816d3..c0736ef66ae 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/AbstractErrorWebExceptionHandler.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/AbstractErrorWebExceptionHandler.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.beans.factory.InitializingBean; @@ -135,7 +136,7 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept * @param request the source request * @return the error */ - protected Throwable getError(ServerRequest request) { + protected @Nullable Throwable getError(ServerRequest request) { return this.errorAttributes.getError(request); } @@ -208,7 +209,7 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept return this.templateAvailabilityProviders.getProvider(viewName, this.applicationContext) != null; } - private Resource resolveResource(String viewName) { + private @Nullable Resource resolveResource(String viewName) { for (String location : this.resources.getStaticLocations()) { try { Resource resource = this.applicationContext.getResource(location); @@ -261,7 +262,7 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept return responseBody.bodyValue(builder.toString()); } - private String htmlEscape(Object input) { + private @Nullable String htmlEscape(@Nullable Object input) { return (input != null) ? HtmlUtils.htmlEscape(input.toString()) : null; } diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/package-info.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/package-info.java index 62ff790caa3..e5a6c9ef9c8 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/package-info.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/error/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for Spring WebFlux error handling. */ +@NullMarked package org.springframework.boot.webflux.autoconfigure.error; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/package-info.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/package-info.java index c32423bbd6f..68d27836e3b 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/package-info.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for reactive web servers and Spring WebFlux. */ +@NullMarked package org.springframework.boot.webflux.autoconfigure; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/error/ErrorAttributes.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/error/ErrorAttributes.java index effa46f8e8b..737256e105d 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/error/ErrorAttributes.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/error/ErrorAttributes.java @@ -19,6 +19,8 @@ package org.springframework.boot.webflux.error; import java.util.Collections; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; @@ -51,7 +53,7 @@ public interface ErrorAttributes { * @param request the source ServerRequest * @return the {@link Exception} that caused the error or {@code null} */ - Throwable getError(ServerRequest request); + @Nullable Throwable getError(ServerRequest request); /** * Store the given error information in the current {@link ServerWebExchange}. diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/error/package-info.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/error/package-info.java index 0a1dd17f025..2707146a68b 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/error/package-info.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/error/package-info.java @@ -17,4 +17,7 @@ /** * Spring WebFlux error handling infrastructure. */ +@NullMarked package org.springframework.boot.webflux.error; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/filter/package-info.java b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/filter/package-info.java index 1ca0ef6eeff..6c637297f93 100644 --- a/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/filter/package-info.java +++ b/module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/filter/package-info.java @@ -17,4 +17,7 @@ /** * Spring Boot specific {@link org.springframework.web.server.WebFilter} implementations. */ +@NullMarked package org.springframework.boot.webflux.filter; + +import org.jspecify.annotations.NullMarked;