diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractGraphQlTesterBuilder.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractGraphQlTesterBuilder.java index 19118200..5fd62028 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractGraphQlTesterBuilder.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractGraphQlTesterBuilder.java @@ -27,6 +27,7 @@ import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.spi.json.JacksonJsonProvider; import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import com.jayway.jsonpath.spi.mapper.MappingProvider; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -40,7 +41,6 @@ import org.springframework.graphql.client.GraphQlClient; import org.springframework.graphql.client.GraphQlTransport; import org.springframework.graphql.support.DocumentSource; import org.springframework.graphql.support.ResourceDocumentSource; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -66,8 +66,7 @@ public abstract class AbstractGraphQlTesterBuilder errorFilter; + private @Nullable Predicate errorFilter; private DocumentSource documentSource; diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultExecutionGraphQlServiceTesterBuilder.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultExecutionGraphQlServiceTesterBuilder.java index 2f33956b..c5440fc5 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultExecutionGraphQlServiceTesterBuilder.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultExecutionGraphQlServiceTesterBuilder.java @@ -24,11 +24,11 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import graphql.ExecutionInput; +import org.jspecify.annotations.Nullable; import org.springframework.core.codec.Decoder; import org.springframework.core.codec.Encoder; import org.springframework.graphql.ExecutionGraphQlService; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -46,11 +46,9 @@ final class DefaultExecutionGraphQlServiceTesterBuilder private final List> executionInputConfigurers; - @Nullable - private Encoder encoder; + private @Nullable Encoder encoder; - @Nullable - private Decoder decoder; + private @Nullable Decoder decoder; DefaultExecutionGraphQlServiceTesterBuilder(ExecutionGraphQlService service) { diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java index e538009b..862b8257 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java @@ -31,6 +31,7 @@ import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.TypeRef; +import org.jspecify.annotations.Nullable; import org.skyscreamer.jsonassert.JSONAssert; import org.springframework.core.ParameterizedTypeReference; @@ -41,7 +42,6 @@ import org.springframework.graphql.ResponseError; import org.springframework.graphql.client.GraphQlTransport; import org.springframework.graphql.support.DefaultGraphQlRequest; import org.springframework.graphql.support.DocumentSource; -import org.springframework.lang.Nullable; import org.springframework.test.util.AssertionErrors; import org.springframework.test.util.JsonPathExpectationsHelper; import org.springframework.util.Assert; @@ -61,8 +61,7 @@ final class DefaultGraphQlTester implements GraphQlTester { private final GraphQlTransport transport; - @Nullable - private final Predicate errorFilter; + private final @Nullable Predicate errorFilter; private final Configuration jsonPathConfig; @@ -120,8 +119,7 @@ final class DefaultGraphQlTester implements GraphQlTester { private final String document; - @Nullable - private String operationName; + private @Nullable String operationName; List fragments = new ArrayList<>(); @@ -173,7 +171,7 @@ final class DefaultGraphQlTester implements GraphQlTester { return this; } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({"ConstantConditions", "NullAway"}) @Override public Response execute() { return DefaultGraphQlTester.this.transport.execute(request()) @@ -374,8 +372,7 @@ final class DefaultGraphQlTester implements GraphQlTester { */ private static final class DefaultPath implements Path { - @Nullable - private final String basePath; + private final @Nullable String basePath; private final String path; diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/EncoderDecoderMappingProvider.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/EncoderDecoderMappingProvider.java index 89103e01..9a074e0a 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/EncoderDecoderMappingProvider.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/EncoderDecoderMappingProvider.java @@ -25,6 +25,7 @@ import java.util.stream.Stream; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.TypeRef; import com.jayway.jsonpath.spi.mapper.MappingProvider; +import org.jspecify.annotations.Nullable; import org.springframework.core.ResolvableType; import org.springframework.core.codec.Decoder; @@ -36,7 +37,6 @@ import org.springframework.http.MediaType; import org.springframework.http.codec.CodecConfigurer; import org.springframework.http.codec.DecoderHttpMessageReader; import org.springframework.http.codec.EncoderHttpMessageWriter; -import org.springframework.lang.Nullable; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; @@ -107,21 +107,18 @@ final class EncoderDecoderMappingProvider implements MappingProvider { } - @Nullable @Override - public T map(Object source, Class targetType, Configuration configuration) { + public @Nullable T map(Object source, Class targetType, Configuration configuration) { return mapToTargetType(source, ResolvableType.forClass(targetType)); } - @Nullable @Override - public T map(Object source, TypeRef targetType, Configuration configuration) { + public @Nullable T map(Object source, TypeRef targetType, Configuration configuration) { return mapToTargetType(source, ResolvableType.forType(targetType.getType())); } @SuppressWarnings("unchecked") - @Nullable - private T mapToTargetType(Object source, ResolvableType targetType) { + private @Nullable T mapToTargetType(Object source, ResolvableType targetType) { DataBufferFactory bufferFactory = DefaultDataBufferFactory.sharedInstance; MimeType mimeType = MimeTypeUtils.APPLICATION_JSON; diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java index 61c51113..f4bbfa33 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.function.Consumer; import java.util.function.Predicate; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import org.springframework.core.ParameterizedTypeReference; @@ -30,7 +31,6 @@ import org.springframework.graphql.ResponseError; import org.springframework.graphql.client.GraphQlTransport; import org.springframework.graphql.support.DocumentSource; import org.springframework.graphql.support.ResourceDocumentSource; -import org.springframework.lang.Nullable; /** * Define a workflow to test GraphQL requests that is independent of the diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerGraphQlTransport.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerGraphQlTransport.java index eaadadf1..fed0528b 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerGraphQlTransport.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerGraphQlTransport.java @@ -20,6 +20,7 @@ package org.springframework.graphql.test.tester; import java.net.URI; import java.util.Collections; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.graphql.ExecutionGraphQlRequest; @@ -28,7 +29,6 @@ import org.springframework.graphql.server.WebGraphQlHandler; import org.springframework.graphql.server.WebGraphQlRequest; import org.springframework.http.HttpHeaders; import org.springframework.http.codec.CodecConfigurer; -import org.springframework.lang.Nullable; /** diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/package-info.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/package-info.java index 67cbae3b..8863502b 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/package-info.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2020 the original author or authors. + * Copyright 2020-2025 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. @@ -17,9 +17,7 @@ /** * GraphQL client testing support. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.test.tester; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlRequest.java index 25b20ed2..5ea13d9f 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/ExecutionGraphQlRequest.java @@ -21,8 +21,7 @@ import java.util.function.BiFunction; import graphql.ExecutionInput; import graphql.execution.ExecutionId; - -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** @@ -70,14 +69,12 @@ public interface ExecutionGraphQlRequest extends GraphQlRequest { /** * Return the configured {@link #executionId(ExecutionId) executionId}. */ - @Nullable - ExecutionId getExecutionId(); + @Nullable ExecutionId getExecutionId(); /** * Return the transport assigned locale value, if any. */ - @Nullable - Locale getLocale(); + @Nullable Locale getLocale(); /** * Provide a {@code BiFunction} to help initialize the {@link ExecutionInput} diff --git a/spring-graphql/src/main/java/org/springframework/graphql/GraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/GraphQlRequest.java index 0e979209..f97acc94 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/GraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/GraphQlRequest.java @@ -18,7 +18,7 @@ package org.springframework.graphql; import java.util.Map; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** @@ -45,11 +45,10 @@ public interface GraphQlRequest { * Return the name of the operation in the {@link #getDocument() document} * to execute, if the document contains multiple operations. */ - @Nullable - String getOperationName(); + @Nullable String getOperationName(); /** - * Return values for variable defined by the operation. + * Return values for variables defined by the operation. */ Map getVariables(); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/GraphQlResponse.java b/spring-graphql/src/main/java/org/springframework/graphql/GraphQlResponse.java index 661b8556..b190f985 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/GraphQlResponse.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/GraphQlResponse.java @@ -20,7 +20,7 @@ package org.springframework.graphql; import java.util.List; import java.util.Map; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Represents a GraphQL response with the result of executing a request operation. @@ -51,8 +51,7 @@ public interface GraphQlResponse { * is not {@link #isValid() valid}. * @param a map or a list */ - @Nullable - T getData(); + @Nullable T getData(); /** * Return errors included in the response. diff --git a/spring-graphql/src/main/java/org/springframework/graphql/ResponseError.java b/spring-graphql/src/main/java/org/springframework/graphql/ResponseError.java index 10297819..05bc9db4 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/ResponseError.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/ResponseError.java @@ -21,8 +21,7 @@ import java.util.Map; import graphql.ErrorClassification; import graphql.language.SourceLocation; - -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Represents a GraphQL response error. @@ -36,8 +35,7 @@ public interface ResponseError { * Return the message with a description of the error intended for the * developer as a guide to understand and correct the error. */ - @Nullable - String getMessage(); + @Nullable String getMessage(); /** * Return a classification for the error that is specific to GraphQL Java. diff --git a/spring-graphql/src/main/java/org/springframework/graphql/ResponseField.java b/spring-graphql/src/main/java/org/springframework/graphql/ResponseField.java index 45c20367..63800021 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/ResponseField.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/ResponseField.java @@ -18,8 +18,9 @@ package org.springframework.graphql; import java.util.List; +import org.jspecify.annotations.Nullable; + import org.springframework.graphql.client.ClientGraphQlResponse; -import org.springframework.lang.Nullable; /** @@ -49,8 +50,7 @@ public interface ResponseField { * @param the expected value type to cast to * @return the value */ - @Nullable - T getValue(); + @Nullable T getValue(); /** * Return all errors that have a path, and it is at above, or below the field path. diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientBuilder.java index 9f414dea..f05e7cc8 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientBuilder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientBuilder.java @@ -23,6 +23,8 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; + import org.springframework.core.codec.Decoder; import org.springframework.core.codec.Encoder; import org.springframework.core.io.ClassPathResource; @@ -33,7 +35,6 @@ import org.springframework.graphql.support.DocumentSource; import org.springframework.graphql.support.ResourceDocumentSource; import org.springframework.http.codec.json.Jackson2JsonDecoder; import org.springframework.http.codec.json.Jackson2JsonEncoder; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -62,14 +63,11 @@ public abstract class AbstractGraphQlClientBuilder jsonEncoder; + private @Nullable Encoder jsonEncoder; - @Nullable - private Decoder jsonDecoder; + private @Nullable Decoder jsonDecoder; - @Nullable - private Duration blockingTimeout; + private @Nullable Duration blockingTimeout; /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientSyncBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientSyncBuilder.java index dd13692a..b969f39f 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientSyncBuilder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientSyncBuilder.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Schedulers; @@ -35,7 +36,6 @@ import org.springframework.graphql.support.DocumentSource; import org.springframework.graphql.support.ResourceDocumentSource; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -65,13 +65,11 @@ public abstract class AbstractGraphQlClientSyncBuilder jsonConverter; + private @Nullable HttpMessageConverter jsonConverter; private Scheduler scheduler = Schedulers.boundedElastic(); - @Nullable - private Duration blockingTimeout; + private @Nullable Duration blockingTimeout; /** * Default constructor for use from subclasses. diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/ClientResponseField.java b/spring-graphql/src/main/java/org/springframework/graphql/client/ClientResponseField.java index 66b5146d..baf0bca8 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/ClientResponseField.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/ClientResponseField.java @@ -19,10 +19,11 @@ package org.springframework.graphql.client; import java.util.List; +import org.jspecify.annotations.Nullable; + import org.springframework.core.ParameterizedTypeReference; import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.ResponseField; -import org.springframework.lang.Nullable; /** * Extends {@link ResponseField} to add options for decoding the field value. @@ -42,16 +43,14 @@ public interface ClientResponseField extends ResponseField { * response is not {@link GraphQlResponse#isValid() valid} or the field has * {@link ResponseField#getErrors() errors}. */ - @Nullable - D toEntity(Class entityType); + @Nullable D toEntity(Class entityType); /** * Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}. * @param the entity type * @param entityType the type to convert to */ - @Nullable - D toEntity(ParameterizedTypeReference entityType); + @Nullable D toEntity(ParameterizedTypeReference entityType); /** * Variant of {@link #toEntity(Class)} to decode to a list of entities. diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/CodecDelegate.java b/spring-graphql/src/main/java/org/springframework/graphql/client/CodecDelegate.java index 841f1219..8dc458e7 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/CodecDelegate.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/CodecDelegate.java @@ -19,6 +19,8 @@ package org.springframework.graphql.client; import java.util.List; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; + import org.springframework.core.ResolvableType; import org.springframework.core.codec.Decoder; import org.springframework.core.codec.Encoder; @@ -108,7 +110,7 @@ final class CodecDelegate { } @SuppressWarnings("ConstantConditions") - GraphQlWebSocketMessage decode(WebSocketMessage webSocketMessage) { + @Nullable GraphQlWebSocketMessage decode(WebSocketMessage webSocketMessage) { DataBuffer buffer = DataBufferUtils.retain(webSocketMessage.getPayload()); return (GraphQlWebSocketMessage) this.decoder.decode(buffer, MESSAGE_TYPE, null, null); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlRequest.java index 8f0e6c83..a8dfb02a 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlRequest.java @@ -20,8 +20,9 @@ package org.springframework.graphql.client; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.jspecify.annotations.Nullable; + import org.springframework.graphql.support.DefaultGraphQlRequest; -import org.springframework.lang.Nullable; /** * Default implementation of {@link ClientGraphQlRequest}. diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientResponseField.java b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientResponseField.java index 870349a9..9a1129d6 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientResponseField.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientResponseField.java @@ -20,6 +20,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ResolvableType; import org.springframework.core.codec.Decoder; @@ -29,7 +31,6 @@ import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.graphql.ResponseError; import org.springframework.graphql.ResponseField; -import org.springframework.lang.Nullable; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; @@ -65,7 +66,7 @@ final class DefaultClientResponseField implements ClientResponseField { } @Override - public T getValue() { + public @Nullable T getValue() { return this.field.getValue(); } @@ -75,12 +76,12 @@ final class DefaultClientResponseField implements ClientResponseField { } @Override - public D toEntity(Class entityType) { + public @Nullable D toEntity(Class entityType) { return toEntity(ResolvableType.forType(entityType)); } @Override - public D toEntity(ParameterizedTypeReference entityType) { + public @Nullable D toEntity(ParameterizedTypeReference entityType) { return toEntity(ResolvableType.forType(entityType)); } @@ -96,9 +97,8 @@ final class DefaultClientResponseField implements ClientResponseField { return (list != null) ? list : Collections.emptyList(); } - @SuppressWarnings("unchecked") - @Nullable - private T toEntity(ResolvableType targetType) { + @SuppressWarnings("unchecked") + private @Nullable T toEntity(ResolvableType targetType) { if (getValue() == null) { if (this.response.isValid() && getErrors().isEmpty()) { return null; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java index 489f323f..96cb7db8 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java @@ -23,13 +23,13 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import org.springframework.core.ParameterizedTypeReference; import org.springframework.graphql.support.DocumentSource; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -47,8 +47,7 @@ final class DefaultGraphQlClient implements GraphQlClient { private final GraphQlClientInterceptor.SubscriptionChain subscriptionChain; - @Nullable - private final Duration blockingTimeout; + private final @Nullable Duration blockingTimeout; DefaultGraphQlClient( @@ -89,7 +88,7 @@ final class DefaultGraphQlClient implements GraphQlClient { return (request) -> Mono.fromCallable(() -> blockingChain.next(request)).subscribeOn(scheduler); } - @SuppressWarnings("DataFlowIssue") + @SuppressWarnings({"DataFlowIssue", "NullAway"}) private static SyncGraphQlClientInterceptor.Chain adaptToBlockingChain( GraphQlClientInterceptor.Chain executeChain, @Nullable Duration blockingTimeout) { @@ -126,8 +125,7 @@ final class DefaultGraphQlClient implements GraphQlClient { private final Mono documentMono; - @Nullable - private String operationName; + private @Nullable String operationName; private final Map variables = new LinkedHashMap<>(); @@ -198,7 +196,7 @@ final class DefaultGraphQlClient implements GraphQlClient { return new DefaultRetrieveSubscriptionSpec(executeSubscription(), path); } - @SuppressWarnings("DataFlowIssue") + @SuppressWarnings({"DataFlowIssue", "NullAway"}) @Override public ClientGraphQlResponse executeSync() { Mono mono = initRequest(); @@ -244,8 +242,7 @@ final class DefaultGraphQlClient implements GraphQlClient { * @throws FieldAccessException in case of an invalid response or any * field error at, above or below the field path */ - @Nullable - protected ClientResponseField getValidField(ClientGraphQlResponse response) { + protected @Nullable ClientResponseField getValidField(ClientGraphQlResponse response) { ClientResponseField field = response.field(this.path); if (!response.isValid() || !field.getErrors().isEmpty()) { throw new FieldAccessException( @@ -267,13 +264,13 @@ final class DefaultGraphQlClient implements GraphQlClient { } @Override - public D toEntity(Class entityType) { + public @Nullable D toEntity(Class entityType) { ClientResponseField field = getValidField(this.response); return (field != null) ? field.toEntity(entityType) : null; } @Override - public D toEntity(ParameterizedTypeReference entityType) { + public @Nullable D toEntity(ParameterizedTypeReference entityType) { ClientResponseField field = getValidField(this.response); return (field != null) ? field.toEntity(entityType) : null; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultRSocketGraphQlClientBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultRSocketGraphQlClientBuilder.java index 68e6fe0a..1b766e63 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultRSocketGraphQlClientBuilder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultRSocketGraphQlClientBuilder.java @@ -25,10 +25,10 @@ import io.rsocket.loadbalance.LoadbalanceTarget; import io.rsocket.transport.ClientTransport; import io.rsocket.transport.netty.client.TcpClientTransport; import io.rsocket.transport.netty.client.WebsocketClientTransport; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; -import org.springframework.lang.Nullable; import org.springframework.messaging.rsocket.RSocketRequester; import org.springframework.messaging.rsocket.RSocketStrategies; import org.springframework.util.Assert; @@ -48,14 +48,11 @@ final class DefaultRSocketGraphQlClientBuilder private final RSocketRequester.Builder requesterBuilder; - @Nullable - private Publisher> targetPublisher; + private @Nullable Publisher> targetPublisher; - @Nullable - private LoadbalanceStrategy loadbalanceStrategy; + private @Nullable LoadbalanceStrategy loadbalanceStrategy; - @Nullable - private ClientTransport clientTransport; + private @Nullable ClientTransport clientTransport; private String route; @@ -169,14 +166,11 @@ final class DefaultRSocketGraphQlClientBuilder private final RSocketRequester.Builder requesterBuilder; - @Nullable - private final ClientTransport clientTransport; + private final @Nullable ClientTransport clientTransport; - @Nullable - private final Publisher> targetPublisher; + private final @Nullable Publisher> targetPublisher; - @Nullable - private final LoadbalanceStrategy loadbalanceStrategy; + private final @Nullable LoadbalanceStrategy loadbalanceStrategy; private final String route; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultWebSocketGraphQlClientBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultWebSocketGraphQlClientBuilder.java index 5bcb3a72..257b438c 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultWebSocketGraphQlClientBuilder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultWebSocketGraphQlClientBuilder.java @@ -22,12 +22,12 @@ import java.util.Arrays; import java.util.List; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.http.HttpHeaders; import org.springframework.http.codec.ClientCodecConfigurer; import org.springframework.http.codec.CodecConfigurer; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.web.reactive.socket.client.WebSocketClient; import org.springframework.web.util.DefaultUriBuilderFactory; @@ -51,8 +51,7 @@ final class DefaultWebSocketGraphQlClientBuilder private final CodecConfigurer codecConfigurer; - @Nullable - private Duration keepAlive; + private @Nullable Duration keepAlive; /** * Constructor to start via {@link WebSocketGraphQlClient#builder(String, WebSocketClient)}. diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/DgsGraphQlClient.java b/spring-graphql/src/main/java/org/springframework/graphql/client/DgsGraphQlClient.java index 90284579..3baf35bc 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/DgsGraphQlClient.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/DgsGraphQlClient.java @@ -25,10 +25,10 @@ import com.netflix.graphql.dgs.client.codegen.BaseProjectionNode; import com.netflix.graphql.dgs.client.codegen.GraphQLQuery; import com.netflix.graphql.dgs.client.codegen.GraphQLQueryRequest; import graphql.schema.Coercing; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -91,14 +91,11 @@ public final class DgsGraphQlClient { private final GraphQLQuery query; - @Nullable - private BaseProjectionNode projectionNode; + private @Nullable BaseProjectionNode projectionNode; - @Nullable - private Map, Coercing> coercingMap; + private @Nullable Map, Coercing> coercingMap; - @Nullable - private Map attributes; + private @Nullable Map attributes; private RequestSpec(GraphQLQuery query) { Assert.notNull(query, "Expected GraphQLQuery"); @@ -243,6 +240,7 @@ public final class DgsGraphQlClient { return initRequestSpec().executeSubscription(); } + @SuppressWarnings("NullAway") private GraphQlClient.RequestSpec initRequestSpec() { Assert.state(this.projectionNode != null || this.coercingMap == null, diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClient.java b/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClient.java index 41afb5db..6f9b9023 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClient.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClient.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; @@ -31,7 +32,6 @@ import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.ResponseField; import org.springframework.graphql.support.DocumentSource; import org.springframework.graphql.support.ResourceDocumentSource; -import org.springframework.lang.Nullable; /** * Define a workflow to execute GraphQL requests that is independent of the @@ -337,16 +337,14 @@ public interface GraphQlClient { * errors} or an {@link GraphQlResponse#isValid() invalid} response; * @see ResponseField#getErrors() */ - @Nullable - D toEntity(Class entityType); + @Nullable D toEntity(Class entityType); /** * Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}. * @param the type to convert to * @param entityType the type to convert to */ - @Nullable - D toEntity(ParameterizedTypeReference entityType); + @Nullable D toEntity(ParameterizedTypeReference entityType); /** * Variant of {@link #toEntity(Class)} to decode to a List of entities. diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClientException.java b/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClientException.java index 9cce8bce..4c2e4115 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClientException.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClientException.java @@ -17,9 +17,10 @@ package org.springframework.graphql.client; +import org.jspecify.annotations.Nullable; + import org.springframework.core.NestedRuntimeException; import org.springframework.graphql.GraphQlRequest; -import org.springframework.lang.Nullable; /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlTransportException.java b/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlTransportException.java index e7f80067..265e3104 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlTransportException.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlTransportException.java @@ -17,8 +17,9 @@ package org.springframework.graphql.client; +import org.jspecify.annotations.Nullable; + import org.springframework.graphql.GraphQlRequest; -import org.springframework.lang.Nullable; /** * Exception raised by a {@link GraphQlTransport} or used to wrap an exception @@ -36,7 +37,7 @@ public class GraphQlTransportException extends GraphQlClientException { * @param request the request that failed at the transport level */ public GraphQlTransportException(@Nullable Throwable cause, GraphQlRequest request) { - super("GraphQlTransport error: " + cause.getMessage(), cause, request); + super("GraphQlTransport error" + ((cause != null) ? ": " + cause.getMessage() : ""), cause, request); } /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/HttpGraphQlTransport.java b/spring-graphql/src/main/java/org/springframework/graphql/client/HttpGraphQlTransport.java index 0d44ba3e..acfeaf9f 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/HttpGraphQlTransport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/HttpGraphQlTransport.java @@ -16,6 +16,7 @@ package org.springframework.graphql.client; +import java.util.Collections; import java.util.Map; import reactor.core.publisher.Flux; @@ -118,7 +119,10 @@ final class HttpGraphQlTransport implements GraphQlTransport { .retrieve() .bodyToFlux(SSE_TYPE) .takeWhile((event) -> "next".equals(event.event())) - .map((event) -> new ResponseMapGraphQlResponse(event.data())); + .map((event) -> { + Map data = (event.data() != null) ? event.data() : Collections.emptyMap(); + return new ResponseMapGraphQlResponse(data); + }); } } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/HttpMessageConverterDelegate.java b/spring-graphql/src/main/java/org/springframework/graphql/client/HttpMessageConverterDelegate.java index 2952a90a..d098ad3e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/HttpMessageConverterDelegate.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/HttpMessageConverterDelegate.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -43,7 +44,6 @@ import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.lang.Nullable; import org.springframework.util.MimeType; @@ -77,8 +77,7 @@ final class HttpMessageConverterDelegate { return new HttpMessageConverterDecoder(converter); } - @Nullable - private static MediaType toMediaType(@Nullable MimeType mimeType) { + private static @Nullable MediaType toMediaType(@Nullable MimeType mimeType) { if (mimeType instanceof MediaType mediaType) { return mediaType; } @@ -126,7 +125,8 @@ final class HttpMessageConverterDelegate { return bufferFactory.wrap(messageAdapter.toByteArray()); } catch (IOException ex) { - throw new EncodingException(ex.getMessage(), ex); + // TODO: revisit + throw new EncodingException("Error while encoding: " + ex.getMessage(), ex); } } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/RSocketGraphQlTransport.java b/spring-graphql/src/main/java/org/springframework/graphql/client/RSocketGraphQlTransport.java index 3d19b614..94963117 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/RSocketGraphQlTransport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/RSocketGraphQlTransport.java @@ -89,7 +89,8 @@ final class RSocketGraphQlTransport implements GraphQlTransport { @SuppressWarnings("unchecked") private Exception decodeErrors(GraphQlRequest request, RejectedException ex) { try { - byte[] errorData = ex.getMessage().getBytes(StandardCharsets.UTF_8); + String errorMessage = (ex.getMessage() != null) ? ex.getMessage() : ""; + byte[] errorData = errorMessage.getBytes(StandardCharsets.UTF_8); List errors = (List) this.jsonDecoder.decode( DefaultDataBufferFactory.sharedInstance.wrap(errorData), LIST_TYPE, null, null); GraphQlResponse response = new ResponseMapGraphQlResponse(Collections.singletonMap("errors", errors)); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/ResponseMapGraphQlResponse.java b/spring-graphql/src/main/java/org/springframework/graphql/client/ResponseMapGraphQlResponse.java index a4d7caad..cebf98d8 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/ResponseMapGraphQlResponse.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/ResponseMapGraphQlResponse.java @@ -24,11 +24,11 @@ import java.util.stream.Collectors; import graphql.ErrorClassification; import graphql.GraphQLError; import graphql.language.SourceLocation; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.ResponseError; import org.springframework.graphql.support.AbstractGraphQlResponse; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -76,7 +76,7 @@ class ResponseMapGraphQlResponse extends AbstractGraphQlResponse { @SuppressWarnings("unchecked") @Override - public T getData() { + public @Nullable T getData() { return (T) this.responseMap.get("data"); } @@ -160,8 +160,7 @@ class ResponseMapGraphQlResponse extends AbstractGraphQlResponse { @Override - @Nullable - public String getMessage() { + public @Nullable String getMessage() { return (String) this.errorMap.get("message"); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/WebSocketGraphQlTransport.java b/spring-graphql/src/main/java/org/springframework/graphql/client/WebSocketGraphQlTransport.java index 02c1ae48..a9c2cb0d 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/WebSocketGraphQlTransport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/WebSocketGraphQlTransport.java @@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import reactor.core.Scannable; import reactor.core.publisher.Flux; import reactor.core.publisher.FluxSink; @@ -41,7 +42,6 @@ import org.springframework.graphql.server.support.GraphQlWebSocketMessage; import org.springframework.graphql.server.support.GraphQlWebSocketMessageType; import org.springframework.http.HttpHeaders; import org.springframework.http.codec.CodecConfigurer; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.web.reactive.socket.CloseStatus; import org.springframework.web.reactive.socket.WebSocketHandler; @@ -68,8 +68,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport { private final Mono graphQlSessionMono; - @Nullable - private final Duration keepAlive; + private final @Nullable Duration keepAlive; WebSocketGraphQlTransport( @@ -167,8 +166,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport { return this.graphQlSessionMono.flatMapMany((session) -> session.executeSubscription(request)); } - @Nullable - Duration getKeepAlive() { + @Nullable Duration getKeepAlive() { return this.keepAlive; } @@ -193,8 +191,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport { private final AtomicBoolean stopped = new AtomicBoolean(); - @Nullable - private final Duration keepAlive; + private final @Nullable Duration keepAlive; GraphQlSessionHandler( @@ -267,6 +264,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport { if (sessionNotInitialized()) { try { GraphQlWebSocketMessage message = this.codecDelegate.decode(webSocketMessage); + Assert.notNull(message, () -> "Cannot decode graphql message from: " + webSocketMessage); Assert.state(message.resolvedType() == GraphQlWebSocketMessageType.CONNECTION_ACK, () -> "Unexpected message before connection_ack: " + message); return this.interceptor.handleConnectionAck(message.getPayload()) @@ -290,6 +288,9 @@ final class WebSocketGraphQlTransport implements GraphQlTransport { else { try { GraphQlWebSocketMessage message = this.codecDelegate.decode(webSocketMessage); + if (message == null) { + throw new IllegalStateException("Cannot decode graphql message from: " + webSocketMessage); + } switch (message.resolvedType()) { case NEXT -> graphQlSession.handleNext(message); case PING -> graphQlSession.sendPong(null); @@ -300,7 +301,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport { "Unexpected message type: '" + message.getType() + "'"); } } - catch (Exception ex) { + catch (Throwable ex) { if (logger.isErrorEnabled()) { logger.error("Closing " + session + ": " + ex); } @@ -654,8 +655,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport { */ private static final class RequestSink { - @Nullable - private FluxSink requestSink; + private @Nullable FluxSink requestSink; private boolean hasSentMessages; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/client/package-info.java index d9fd92bd..bb7ba71b 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -18,9 +18,7 @@ * This package contains a {@link org.springframework.graphql.client.GraphQlClient} * along with HTTP and WebSocket extensions. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.client; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/ArgumentValue.java b/spring-graphql/src/main/java/org/springframework/graphql/data/ArgumentValue.java index 5ce87f2f..3f5e26ab 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/ArgumentValue.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/ArgumentValue.java @@ -20,7 +20,8 @@ package org.springframework.graphql.data; import java.util.Optional; import java.util.function.Consumer; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -54,8 +55,7 @@ public final class ArgumentValue { private static final ArgumentValue OMITTED = new ArgumentValue<>(null, true); - @Nullable - private final T value; + private final @Nullable T value; private final boolean omitted; @@ -94,8 +94,7 @@ public final class ArgumentValue { /** * Return the contained value, or {@code null}. */ - @Nullable - public T value() { + public @Nullable T value() { return this.value; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/GraphQlArgumentBinder.java b/spring-graphql/src/main/java/org/springframework/graphql/data/GraphQlArgumentBinder.java index 20f37189..1150f33f 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/GraphQlArgumentBinder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/GraphQlArgumentBinder.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Optional; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeanUtils; @@ -40,7 +41,7 @@ import org.springframework.core.ResolvableType; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper; -import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.validation.AbstractBindingResult; @@ -76,8 +77,7 @@ import org.springframework.validation.FieldError; */ public class GraphQlArgumentBinder { - @Nullable - private final SimpleTypeConverter typeConverter; + private final @Nullable SimpleTypeConverter typeConverter; private final boolean fallBackOnDirectFieldAccess; @@ -95,8 +95,7 @@ public class GraphQlArgumentBinder { this.fallBackOnDirectFieldAccess = fallBackOnDirectFieldAccess; } - @Nullable - private static SimpleTypeConverter initTypeConverter(@Nullable ConversionService conversionService) { + private static @Nullable SimpleTypeConverter initTypeConverter(@Nullable ConversionService conversionService) { if (conversionService == null) { // Not thread-safe when using PropertyEditors return null; @@ -119,8 +118,7 @@ public class GraphQlArgumentBinder { * @throws BindException containing one or more accumulated errors from * matching and/or converting arguments to the target Object */ - @Nullable - public Object bind( + public @Nullable Object bind( DataFetchingEnvironment environment, @Nullable String name, ResolvableType targetType) throws BindException { @@ -139,8 +137,7 @@ public class GraphQlArgumentBinder { * @param targetType the type of Object to create * @since 1.3.0 */ - @Nullable - public Object bind(@Nullable Object rawValue, boolean isOmitted, ResolvableType targetType) throws BindException { + public @Nullable Object bind(@Nullable Object rawValue, boolean isOmitted, ResolvableType targetType) throws BindException { ArgumentsBindingResult bindingResult = new ArgumentsBindingResult(targetType); Class targetClass = targetType.resolve(Object.class); Object value = bindRawValue("$", rawValue, isOmitted, targetType, targetClass, bindingResult); @@ -168,8 +165,7 @@ public class GraphQlArgumentBinder { * a {@link BindException} at the end to record as many errors as possible */ @SuppressWarnings({"ConstantConditions", "unchecked"}) - @Nullable - private Object bindRawValue( + private @Nullable Object bindRawValue( String name, @Nullable Object rawValue, boolean isOmitted, ResolvableType targetType, Class targetClass, ArgumentsBindingResult bindingResult) { @@ -179,6 +175,7 @@ public class GraphQlArgumentBinder { if (isOptional || isArgumentValue) { targetType = targetType.getNested(2); targetClass = targetType.resolve(); + Assert.state(targetClass != null, "Could not resolve target type for: " + targetType); } Object value; @@ -229,8 +226,7 @@ public class GraphQlArgumentBinder { return collection; } - @Nullable - private Object bindMap( + private @Nullable Object bindMap( String name, Map rawMap, ResolvableType targetType, Class targetClass, ArgumentsBindingResult bindingResult) { @@ -272,13 +268,12 @@ public class GraphQlArgumentBinder { return map; } - @Nullable - private Object bindViaConstructorAndSetters(Constructor constructor, + private @Nullable Object bindViaConstructorAndSetters(Constructor constructor, Map rawMap, ResolvableType ownerType, ArgumentsBindingResult bindingResult) { String[] paramNames = BeanUtils.getParameterNames(constructor); Class[] paramTypes = constructor.getParameterTypes(); - Object[] constructorArguments = new Object[paramTypes.length]; + @Nullable Object[] constructorArguments = new Object[paramTypes.length]; for (int i = 0; i < paramNames.length; i++) { String name = paramNames[i]; @@ -359,8 +354,7 @@ public class GraphQlArgumentBinder { } @SuppressWarnings("unchecked") - @Nullable - private T convertValue( + private @Nullable T convertValue( String name, @Nullable Object rawValue, ResolvableType type, Class clazz, ArgumentsBindingResult bindingResult) { @@ -398,12 +392,12 @@ public class GraphQlArgumentBinder { } @Override - public Object getTarget() { + public @Nullable Object getTarget() { return null; } @Override - protected Object getActualFieldValue(String field) { + protected @Nullable Object getActualFieldValue(String field) { return null; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntitiesDataFetcher.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntitiesDataFetcher.java index 51116a74..51c7a33b 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntitiesDataFetcher.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntitiesDataFetcher.java @@ -35,11 +35,11 @@ import graphql.execution.ExecutionStepInfo; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.DelegatingDataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.graphql.data.method.annotation.support.HandlerDataFetcherExceptionResolver; import org.springframework.graphql.execution.ErrorType; -import org.springframework.lang.Nullable; /** * DataFetcher that handles the "_entities" query by invoking @@ -155,7 +155,7 @@ final class EntitiesDataFetcher implements DataFetcher resolveException( Throwable ex, DataFetchingEnvironment env, @Nullable EntityHandlerMethod handlerMethod, int index) { - Throwable theEx = (ex instanceof CompletionException) ? ex.getCause() : ex; + Throwable theEx = unwrapException(ex); DataFetchingEnvironment theEnv = new IndexedDataFetchingEnvironment(env, index); Object handler = (handlerMethod != null) ? handlerMethod.getBean() : null; @@ -164,6 +164,13 @@ final class EntitiesDataFetcher implements DataFetcher createDefaultError(theEx, theEnv))); } + private Throwable unwrapException(Throwable exception) { + if (exception instanceof CompletionException completionException) { + return (completionException.getCause() != null) ? completionException.getCause() : exception; + } + return exception; + } + private ErrorContainer createDefaultError(Throwable ex, DataFetchingEnvironment env) { ErrorType errorType = (ex instanceof RepresentationException representationEx) ? diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityArgumentMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityArgumentMethodArgumentResolver.java index 28202284..2f36d553 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityArgumentMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityArgumentMethodArgumentResolver.java @@ -22,12 +22,12 @@ import java.util.Map; import graphql.schema.DataFetchingEnvironment; import graphql.schema.DelegatingDataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.ResolvableType; import org.springframework.graphql.data.GraphQlArgumentBinder; import org.springframework.graphql.data.method.annotation.Argument; import org.springframework.graphql.data.method.annotation.support.ArgumentMethodArgumentResolver; -import org.springframework.lang.Nullable; import org.springframework.validation.BindException; /** @@ -47,7 +47,7 @@ final class EntityArgumentMethodArgumentResolver extends ArgumentMethodArgumentR @Override - protected Object doBind( + protected @Nullable Object doBind( DataFetchingEnvironment environment, String name, ResolvableType targetType) throws BindException { if (environment instanceof EntityDataFetchingEnvironment entityEnv) { @@ -56,7 +56,7 @@ final class EntityArgumentMethodArgumentResolver extends ArgumentMethodArgumentR else if (environment instanceof EntityBatchDataFetchingEnvironment batchEnv) { name = dePluralize(name); targetType = targetType.getNested(2); - List values = new ArrayList<>(); + List<@Nullable Object> values = new ArrayList<>(); for (Map representation : batchEnv.getRepresentations()) { values.add(doBind(name, targetType, representation)); } @@ -67,8 +67,7 @@ final class EntityArgumentMethodArgumentResolver extends ArgumentMethodArgumentR } } - @Nullable - private Object doBind(String name, ResolvableType targetType, Map entityMap) throws BindException { + private @Nullable Object doBind(String name, ResolvableType targetType, Map entityMap) throws BindException { Object rawValue = entityMap.get(name); boolean isOmitted = !entityMap.containsKey(name); return getArgumentBinder().bind(rawValue, isOmitted, targetType); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityArgumentsMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityArgumentsMethodArgumentResolver.java index 767d7c64..6a0e0de7 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityArgumentsMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityArgumentsMethodArgumentResolver.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; @@ -60,7 +61,7 @@ final class EntityArgumentsMethodArgumentResolver implements HandlerMethodArgume } @Override - public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment env) throws Exception { + public @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment env) throws Exception { ResolvableType targetType = ResolvableType.forMethodParameter(parameter); if (env instanceof EntityDataFetchingEnvironment entityEnv) { return this.argumentBinder.bind(entityEnv.getRepresentation(), false, targetType); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityHandlerMethod.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityHandlerMethod.java index 578c21df..830baebd 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityHandlerMethod.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityHandlerMethod.java @@ -21,12 +21,12 @@ import java.util.Map; import java.util.concurrent.Executor; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite; import org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethodSupport; import org.springframework.graphql.execution.ReactiveAdapterRegistryHelper; -import org.springframework.lang.Nullable; /** * Invokable controller method to fetch a federated entity. @@ -63,7 +63,7 @@ final class EntityHandlerMethod extends DataFetcherHandlerMethodSupport { } private Mono doInvoke(DataFetchingEnvironment env) { - Object[] args; + @Nullable Object[] args; try { args = getMethodArgumentValues(env); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java index c6182917..ec9efbc9 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java @@ -32,6 +32,7 @@ import graphql.schema.GraphQLSchema; import graphql.schema.TypeResolver; import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.TypeDefinitionRegistry; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationContext; import org.springframework.context.expression.BeanFactoryResolver; @@ -53,7 +54,6 @@ import org.springframework.graphql.data.method.annotation.support.LocalContextVa import org.springframework.graphql.data.method.annotation.support.PrincipalMethodArgumentResolver; import org.springframework.graphql.execution.ClassNameTypeResolver; import org.springframework.graphql.execution.GraphQlSource.SchemaResourceBuilder; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -73,8 +73,7 @@ import org.springframework.util.StringUtils; public final class FederationSchemaFactory extends AnnotatedControllerDetectionSupport { - @Nullable - private TypeResolver typeResolver; + private @Nullable TypeResolver typeResolver; private final Map handlerMethods = new LinkedHashMap<>(); @@ -141,8 +140,7 @@ public final class FederationSchemaFactory @Override - @Nullable - protected EntityMappingInfo getMappingInfo(Method method, Object handler, Class handlerType) { + protected @Nullable EntityMappingInfo getMappingInfo(Method method, Object handler, Class handlerType) { EntityMapping mapping = AnnotatedElementUtils.findMergedAnnotation(method, EntityMapping.class); if (mapping == null) { return null; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/RepresentationException.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/RepresentationException.java index d69b1162..94d17e08 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/RepresentationException.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/RepresentationException.java @@ -19,9 +19,10 @@ package org.springframework.graphql.data.federation; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.graphql.data.method.HandlerMethod; import org.springframework.graphql.execution.ErrorType; -import org.springframework.lang.Nullable; /** * Raised when a representation could not be resolved because: @@ -41,8 +42,7 @@ public class RepresentationException extends RuntimeException { private final Map representation; - @Nullable - private final HandlerMethod handlerMethod; + private final @Nullable HandlerMethod handlerMethod; private final ErrorType errorType; @@ -62,16 +62,14 @@ public class RepresentationException extends RuntimeException { /** * Return the entity "representation" input map. */ - @Nullable - public Map getRepresentation() { + public @Nullable Map getRepresentation() { return this.representation; } /** * Return the mapped controller method, or {@code null} if it could not be mapped. */ - @Nullable - public HandlerMethod getHandlerMethod() { + public @Nullable HandlerMethod getHandlerMethod() { return this.handlerMethod; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/package-info.java index b82ed4fe..ef254c4b 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -21,9 +21,7 @@ * via {@link org.springframework.graphql.data.federation.EntityMapping @EntityMapping} * controller methods. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.data.federation; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethod.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethod.java index c493bdec..659a5527 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethod.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethod.java @@ -26,6 +26,7 @@ import java.util.stream.IntStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.BeanFactory; import org.springframework.core.BridgeMethodResolver; @@ -33,7 +34,6 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.SynthesizingMethodParameter; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -61,8 +61,7 @@ public class HandlerMethod { private final Object bean; - @Nullable - private final BeanFactory beanFactory; + private final @Nullable BeanFactory beanFactory; private final Class beanType; @@ -72,8 +71,7 @@ public class HandlerMethod { private final MethodParameter[] parameters; - @Nullable - private volatile List interfaceParameterAnnotations; + private volatile @Nullable List interfaceParameterAnnotations; /** @@ -221,8 +219,7 @@ public class HandlerMethod { * @return the annotation, or {@code null} if none found * @see AnnotatedElementUtils#findMergedAnnotation */ - @Nullable - public A getMethodAnnotation(Class annotationType) { + public @Nullable A getMethodAnnotation(Class annotationType) { return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType); } @@ -242,9 +239,8 @@ public class HandlerMethod { */ public HandlerMethod createWithResolvedBean() { Object handler = this.bean; - if (this.bean instanceof String) { + if (this.bean instanceof String beanName) { Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory"); - String beanName = (String) this.bean; handler = this.beanFactory.getBean(beanName); } return new HandlerMethod(this, handler); @@ -317,9 +313,7 @@ public class HandlerMethod { // Support methods for use in "InvocableHandlerMethod" sub-class variants.. - - @Nullable - protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) { + protected static @Nullable Object findProvidedArgument(MethodParameter parameter, Object... providedArgs) { if (!ObjectUtils.isEmpty(providedArgs)) { for (Object providedArg : providedArgs) { if (parameter.getParameterType().isInstance(providedArg)) { @@ -345,7 +339,7 @@ public class HandlerMethod { * @param targetBean the bean instance * @param args the method arguments */ - protected void assertTargetBean(Method method, Object targetBean, Object[] args) { + protected void assertTargetBean(Method method, Object targetBean, @Nullable Object[] args) { Class methodDeclaringClass = method.getDeclaringClass(); Class targetBeanClass = targetBean.getClass(); if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) { @@ -357,12 +351,18 @@ public class HandlerMethod { } } - protected String formatInvokeError(String text, Object[] args) { + protected String formatInvokeError(String text, @Nullable Object[] args) { String formattedArgs = IntStream.range(0, args.length) - .mapToObj((i) -> (args[i] != null) ? - "[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" : - "[" + i + "] [null]") + .mapToObj((i) -> { + Object arg = args[i]; + if (arg != null) { + return "[" + i + "] [type=" + arg.getClass().getName() + "] [value=" + arg + "]"; + } + else { + return "[" + i + "] [null]"; + } + }) .collect(Collectors.joining(",\n", " ", " ")); return text + "\n" + @@ -377,8 +377,7 @@ public class HandlerMethod { */ protected class HandlerMethodParameter extends SynthesizingMethodParameter { - @Nullable - private volatile Annotation[] combinedAnnotations; + private volatile Annotation @Nullable [] combinedAnnotations; public HandlerMethodParameter(int index) { super(HandlerMethod.this.bridgedMethod, index); @@ -394,7 +393,7 @@ public class HandlerMethod { } @Override - public T getMethodAnnotation(Class annotationType) { + public @Nullable T getMethodAnnotation(Class annotationType) { return HandlerMethod.this.getMethodAnnotation(annotationType); } @@ -404,6 +403,7 @@ public class HandlerMethod { } @Override + @SuppressWarnings("NullAway") public Annotation[] getParameterAnnotations() { Annotation[] anns = this.combinedAnnotations; if (anns == null) { @@ -450,8 +450,7 @@ public class HandlerMethod { */ private class ReturnValueMethodParameter extends HandlerMethodParameter { - @Nullable - private final Object returnValue; + private final @Nullable Object returnValue; ReturnValueMethodParameter(@Nullable Object returnValue) { super(-1); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolver.java index e4cd87ce..e1a216af 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolver.java @@ -17,9 +17,9 @@ package org.springframework.graphql.data.method; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; -import org.springframework.lang.Nullable; /** * Strategy interface for resolving method parameters into argument values in @@ -50,7 +50,6 @@ public interface HandlerMethodArgumentResolver { * requires asynchronous resolution. * @throws Exception in case of errors with the preparation of argument values */ - @Nullable - Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception; + @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolverComposite.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolverComposite.java index b94addd1..beae9ff7 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolverComposite.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolverComposite.java @@ -23,9 +23,9 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; -import org.springframework.lang.Nullable; /** * Container for a list of resolvers that looks for one that supports a given @@ -74,8 +74,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu * @throws IllegalArgumentException if no suitable argument resolver is found */ @Override - @Nullable - public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { + public @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { throw new IllegalArgumentException("Unsupported parameter [" + parameter + "]."); @@ -88,8 +87,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu * the given method parameter. * @param parameter the method parameter */ - @Nullable - public HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { + public @Nullable HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { return this.argumentResolverCache.computeIfAbsent(parameter, (p) -> { for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { if (resolver.supportsParameter(parameter)) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java index b551a7d8..5ec5c896 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java @@ -27,13 +27,13 @@ import java.util.concurrent.Executor; import graphql.GraphQLContext; import io.micrometer.context.ContextSnapshot; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.core.CoroutinesUtils; import org.springframework.core.KotlinDetector; import org.springframework.data.util.KotlinReflectionUtils; import org.springframework.graphql.execution.ContextPropagationHelper; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -48,8 +48,7 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod { private static final Object NO_VALUE = new Object(); - @Nullable - private final Executor executor; + private final @Nullable Executor executor; private final boolean hasCallableReturnValue; @@ -97,8 +96,7 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod { * if the invocation fails. */ @SuppressWarnings("ReactiveStreamsUnusedPublisher") - @Nullable - protected Object doInvoke(GraphQLContext graphQLContext, Object... argValues) { + protected @Nullable Object doInvoke(GraphQLContext graphQLContext, @Nullable Object... argValues) { if (logger.isTraceEnabled()) { logger.trace("Invoking " + getBridgedMethod().getName() + "(" + Arrays.toString(argValues) + ")"); } @@ -134,7 +132,7 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod { } @SuppressWarnings({"ReactiveStreamsUnusedPublisher", "unchecked"}) - private static Object invokeSuspendingFunction(Object bean, Method method, Object[] argValues) { + private static Object invokeSuspendingFunction(Object bean, Method method, @Nullable Object[] argValues) { Object result = CoroutinesUtils.invokeSuspendingFunction(method, bean, argValues); // Support use of DataLoader from suspending function @@ -148,9 +146,10 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod { @SuppressWarnings("DataFlowIssue") private CompletableFuture adaptCallable( - GraphQLContext graphQLContext, Callable result, Method method, Object[] argValues) { + GraphQLContext graphQLContext, Callable result, Method method, @Nullable Object[] argValues) { CompletableFuture future = new CompletableFuture<>(); + Assert.state(this.executor != null, "No Executor configured for Callable return values"); this.executor.execute(() -> { try { ContextSnapshot snapshot = ContextPropagationHelper.captureFrom(graphQLContext); @@ -171,14 +170,14 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod { } private IllegalStateException processIllegalArgumentException( - Object[] argValues, IllegalArgumentException ex, Method method) { + @Nullable Object[] argValues, IllegalArgumentException ex, Method method) { assertTargetBean(method, getBean(), argValues); String text = (ex.getMessage() != null) ? ex.getMessage() : "Illegal argument"; return new IllegalStateException(formatInvokeError(text, argValues), ex); } - private Throwable processInvocationTargetException(Object[] argValues, InvocationTargetException ex) { + private Throwable processInvocationTargetException(@Nullable Object[] argValues, InvocationTargetException ex) { // Unwrap for DataFetcherExceptionResolvers ... Throwable targetException = ex.getTargetException(); if (targetException instanceof Error || targetException instanceof Exception) { @@ -193,8 +192,8 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod { * useful when at least one of the values is a {@link Mono} * @param args the arguments to be resolved asynchronously */ - @SuppressWarnings("unchecked") - protected Mono toArgsMono(Object[] args) { + @SuppressWarnings({"unchecked", "NullAway"}) + protected Mono<@Nullable Object[]> toArgsMono(@Nullable Object[] args) { List> monoList = new ArrayList<>(); for (Object arg : args) { Mono argMono = ((arg instanceof Mono) ? (Mono) arg : Mono.justOrEmpty(arg)); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/package-info.java index f2145028..0a98173d 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -18,9 +18,7 @@ * Annotations for binding data fetching methods to GraphQL schema queries, * mutations, subscriptions, and fields. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.data.method.annotation; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java index 1df1b6ec..42e3128d 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java @@ -45,6 +45,7 @@ import graphql.schema.GraphQLCodeRegistry; import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.TypeDefinitionRegistry; import org.dataloader.DataLoader; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -76,7 +77,6 @@ import org.springframework.graphql.execution.ReactiveAdapterRegistryHelper; import org.springframework.graphql.execution.RuntimeWiringConfigurer; import org.springframework.graphql.execution.SelfDescribingDataFetcher; import org.springframework.graphql.execution.SubscriptionPublisherException; -import org.springframework.lang.Nullable; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -129,8 +129,7 @@ public class AnnotatedControllerConfigurer private final InterfaceMappingHelper interfaceMappingHelper = new InterfaceMappingHelper(); - @Nullable - private ValidationHelper validationHelper; + private @Nullable ValidationHelper validationHelper; /** @@ -252,7 +251,7 @@ public class AnnotatedControllerConfigurer } @Override - protected DataFetcherMappingInfo getMappingInfo(Method method, Object handler, Class handlerType) { + protected @Nullable DataFetcherMappingInfo getMappingInfo(Method method, Object handler, Class handlerType) { Set annotations = AnnotatedElementUtils.findAllMergedAnnotations( method, new LinkedHashSet<>(Arrays.asList(BatchMapping.class, SchemaMapping.class))); @@ -434,13 +433,11 @@ public class AnnotatedControllerConfigurer private final HandlerMethodArgumentResolverComposite argumentResolvers; - @Nullable - private final BiConsumer methodValidationHelper; + private final @Nullable BiConsumer methodValidationHelper; private final HandlerDataFetcherExceptionResolver exceptionResolver; - @Nullable - private final Executor executor; + private final @Nullable Executor executor; private final boolean invokeAsync; @@ -511,7 +508,7 @@ public class AnnotatedControllerConfigurer @Override @SuppressWarnings({"ConstantConditions", "ReactiveStreamsUnusedPublisher"}) - public Object get(DataFetchingEnvironment environment) throws Exception { + public @Nullable Object get(DataFetchingEnvironment environment) throws Exception { DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod( getHandlerMethod(), this.argumentResolvers, this.methodValidationHelper, @@ -527,9 +524,8 @@ public class AnnotatedControllerConfigurer } @SuppressWarnings({"unchecked", "ReactiveStreamsUnusedPublisher"}) - @Nullable - private Object applyExceptionHandling( - DataFetchingEnvironment env, DataFetcherHandlerMethod handlerMethod, Object result) { + private @Nullable Object applyExceptionHandling( + DataFetchingEnvironment env, DataFetcherHandlerMethod handlerMethod, @Nullable Object result) { if (this.subscription) { return ReactiveAdapterRegistryHelper.toSubscriptionFlux(result) @@ -603,7 +599,8 @@ public class AnnotatedControllerConfigurer @Override public Object get(DataFetchingEnvironment env) { DataLoader dataLoader = env.getDataLoader(this.dataLoaderKey); - Assert.state(dataLoader != null, "No DataLoader for key '" + this.dataLoaderKey + "'"); + Assert.state(dataLoader != null, () -> "No DataLoader for key '" + this.dataLoaderKey + "'"); + Assert.state(env.getSource() != null, () -> "Missing Source in environment"); return ((env.getLocalContext() != null) ? dataLoader.load(env.getSource(), env.getLocalContext()) : dataLoader.load(env.getSource())); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerDetectionSupport.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerDetectionSupport.java index 1e059021..7c1d25a2 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerDetectionSupport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerDetectionSupport.java @@ -30,6 +30,7 @@ import java.util.function.Predicate; import graphql.schema.DataFetcher; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.InitializingBean; @@ -46,7 +47,6 @@ import org.springframework.format.support.FormattingConversionService; import org.springframework.graphql.data.method.HandlerMethod; import org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite; import org.springframework.graphql.execution.DataFetcherExceptionResolver; -import org.springframework.lang.Nullable; import org.springframework.scheduling.SchedulingTaskExecutor; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; @@ -90,20 +90,16 @@ public abstract class AnnotatedControllerDetectionSupport implements Applicat private boolean fallBackOnDirectFieldAccess; - @Nullable - private AnnotatedControllerExceptionResolver exceptionResolver; + private @Nullable AnnotatedControllerExceptionResolver exceptionResolver; - @Nullable - private Executor executor; + private @Nullable Executor executor; private Predicate blockingMethodPredicate = (virtualThreadsPresent) ? new BlockingHandlerMethodPredicate() : ((method) -> false); - @Nullable - private HandlerMethodArgumentResolverComposite argumentResolvers; + private @Nullable HandlerMethodArgumentResolverComposite argumentResolvers; - @Nullable - private ApplicationContext applicationContext; + private @Nullable ApplicationContext applicationContext; /** @@ -171,8 +167,7 @@ public abstract class AnnotatedControllerDetectionSupport implements Applicat /** * Return the {@link #setExecutor(Executor) configured Executor}. */ - @Nullable - public Executor getExecutor() { + public @Nullable Executor getExecutor() { return this.executor; } @@ -207,8 +202,7 @@ public abstract class AnnotatedControllerDetectionSupport implements Applicat this.applicationContext = applicationContext; } - @Nullable - protected ApplicationContext getApplicationContext() { + protected @Nullable ApplicationContext getApplicationContext() { return this.applicationContext; } @@ -273,8 +267,7 @@ public abstract class AnnotatedControllerDetectionSupport implements Applicat return map.values(); } - @Nullable - protected abstract M getMappingInfo(Method method, Object handler, Class handlerType); + protected abstract @Nullable M getMappingInfo(Method method, Object handler, Class handlerType); protected HandlerMethod createHandlerMethod(Method originalMethod, Object handler, Class handlerType) { Method method = AopUtils.selectInvocableMethod(originalMethod, handlerType); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolver.java index 1ddc505d..45f3c250 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolver.java @@ -31,6 +31,7 @@ import graphql.GraphQLError; import graphql.schema.DataFetchingEnvironment; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.context.ApplicationContext; @@ -44,7 +45,6 @@ import org.springframework.graphql.data.method.HandlerMethod; import org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite; import org.springframework.graphql.data.method.annotation.GraphQlExceptionHandler; import org.springframework.graphql.execution.DataFetcherExceptionResolver; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; @@ -204,7 +204,7 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx } } - if (methodReturnValueAdapter == null) { + if (methodReturnValueAdapter == null || controllerOrAdvice == null) { return Mono.empty(); } @@ -267,7 +267,7 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx */ private static final class MethodResolver { - @SuppressWarnings("DataFlowIssue") + @SuppressWarnings({"DataFlowIssue", "NullAway"}) private static final MethodReturnValueAdapter NO_MATCH = new MethodReturnValueAdapter(ReflectionUtils.findMethod(MethodResolver.class, "noMatch")); @@ -287,8 +287,7 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx * @param exception the exception * @return the exception handler to use, or {@code null} if no match */ - @Nullable - MethodReturnValueAdapter resolveMethod(Throwable exception) { + @Nullable MethodReturnValueAdapter resolveMethod(Throwable exception) { MethodReturnValueAdapter method = resolveMethodByExceptionType(exception.getClass()); if (method == null) { Throwable cause = exception.getCause(); @@ -299,8 +298,7 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx return method; } - @Nullable - private MethodReturnValueAdapter resolveMethodByExceptionType(Class exceptionType) { + private @Nullable MethodReturnValueAdapter resolveMethodByExceptionType(Class exceptionType) { MethodReturnValueAdapter method = this.resolvedExceptionCache.get(exceptionType); if (method == null) { method = getMappedMethod(exceptionType); @@ -309,6 +307,7 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx return (method != NO_MATCH) ? method : null; } + @SuppressWarnings("NullAway") private MethodReturnValueAdapter getMappedMethod(Class exceptionType) { List> matches = new ArrayList<>(); for (Class mappedException : this.exceptionMappings.keySet()) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java index f7366853..67ddd4c1 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java @@ -17,6 +17,7 @@ package org.springframework.graphql.data.method.annotation.support; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; @@ -24,7 +25,6 @@ import org.springframework.graphql.data.ArgumentValue; import org.springframework.graphql.data.GraphQlArgumentBinder; import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; import org.springframework.graphql.data.method.annotation.Argument; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.validation.BindException; @@ -76,7 +76,7 @@ public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentReso } @Override - public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { + public @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { String name = getArgumentName(parameter); ResolvableType targetType = ResolvableType.forMethodParameter(parameter); return doBind(environment, name, targetType); @@ -89,8 +89,7 @@ public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentReso * @param targetType the type of Object to create * @since 1.3.0 */ - @Nullable - protected Object doBind( + protected @Nullable Object doBind( DataFetchingEnvironment environment, String name, ResolvableType targetType) throws BindException { return this.argumentBinder.bind(environment, name, targetType); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentsMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentsMethodArgumentResolver.java index 3291fb8c..69b61a3b 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentsMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentsMethodArgumentResolver.java @@ -17,6 +17,7 @@ package org.springframework.graphql.data.method.annotation.support; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; @@ -56,7 +57,7 @@ public class ArgumentsMethodArgumentResolver implements HandlerMethodArgumentRes } @Override - public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { + public @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter); return this.argumentBinder.bind(environment, null, resolvableType); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolver.java index 6a077057..1dbcf8c7 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolver.java @@ -19,6 +19,7 @@ package org.springframework.graphql.data.method.annotation.support; import java.lang.annotation.Annotation; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; @@ -30,7 +31,6 @@ import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; -import org.springframework.lang.Nullable; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.context.ReactiveSecurityContextHolder; @@ -79,8 +79,7 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg * directly on the {@link MethodParameter} or on a custom annotation that * is meta-annotated with it. */ - @Nullable - private static AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) { + private static @Nullable AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) { AuthenticationPrincipal annotation = parameter.getParameterAnnotation(AuthenticationPrincipal.class); if (annotation != null) { return annotation; @@ -96,7 +95,7 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg } @Override - public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { + public @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { return getCurrentAuthentication(parameter) .mapNotNull((auth) -> resolvePrincipal(parameter, auth.getPrincipal())) .transform((argument) -> isPublisherOrMono(parameter) ? Mono.just(argument) : argument); @@ -113,20 +112,18 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg return (value instanceof Authentication auth) ? Mono.just(auth) : (Mono) value; } - @Nullable - private Object resolvePrincipal(MethodParameter parameter, Object principal) { + private @Nullable Object resolvePrincipal(MethodParameter parameter, Object principal) { AuthenticationPrincipal annotation = findMethodAnnotation(parameter); - String expressionValue = annotation.expression(); - if (StringUtils.hasLength(expressionValue)) { + if (annotation != null && StringUtils.hasLength(annotation.expression())) { StandardEvaluationContext context = new StandardEvaluationContext(); context.setRootObject(principal); context.setVariable("this", principal); context.setBeanResolver(this.beanResolver); - Expression expression = this.parser.parseExpression(expressionValue); + Expression expression = this.parser.parseExpression(annotation.expression()); principal = expression.getValue(context); } if (isInvalidType(parameter, principal)) { - if (annotation.errorOnInvalidType()) { + if (annotation != null && annotation.errorOnInvalidType()) { throw new ClassCastException(principal + " is not assignable to " + parameter.getParameterType()); } return null; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/BatchLoaderHandlerMethod.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/BatchLoaderHandlerMethod.java index 7e5610f9..bfff3caf 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/BatchLoaderHandlerMethod.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/BatchLoaderHandlerMethod.java @@ -25,6 +25,7 @@ import java.util.concurrent.Executor; import graphql.GraphQLContext; import org.dataloader.BatchLoaderEnvironment; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -36,7 +37,6 @@ import org.springframework.graphql.data.method.HandlerMethod; import org.springframework.graphql.data.method.InvocableHandlerMethodSupport; import org.springframework.graphql.data.method.annotation.ContextValue; import org.springframework.graphql.execution.ReactiveAdapterRegistryHelper; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -91,15 +91,16 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport { * @param the type of values in the map * @return a {@code Mono} with map of key-value pairs. */ - @Nullable - public Mono> invokeForMap(Collection keys, BatchLoaderEnvironment environment) { - Object[] args = getMethodArgumentValues(keys, environment); + public @Nullable Mono> invokeForMap(Collection keys, BatchLoaderEnvironment environment) { + @Nullable Object[] args = getMethodArgumentValues(keys, environment); + GraphQLContext context = environment.getContext(); + Assert.notNull(context, "No GraphQLContext available"); if (doesNotHaveAsyncArgs(args)) { - Object result = doInvoke(environment.getContext(), args); + Object result = doInvoke(context, args); return ReactiveAdapterRegistryHelper.toMono(result); } return toArgsMono(args).flatMap((argValues) -> { - Object result = doInvoke(environment.getContext(), argValues); + Object result = doInvoke(context, argValues); return ReactiveAdapterRegistryHelper.toMono(result); }); } @@ -113,27 +114,28 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport { * @return a {@code Flux} of values. */ public Flux invokeForIterable(Collection keys, BatchLoaderEnvironment environment) { - Object[] args = getMethodArgumentValues(keys, environment); + @Nullable Object[] args = getMethodArgumentValues(keys, environment); + GraphQLContext context = environment.getContext(); + Assert.notNull(context, "No GraphQLContext available"); if (doesNotHaveAsyncArgs(args)) { - Object result = doInvoke(environment.getContext(), args); + Object result = doInvoke(context, args); return ReactiveAdapterRegistryHelper.toFluxFromCollection(result); } return toArgsMono(args).flatMapMany((resolvedArgs) -> { - Object result = doInvoke(environment.getContext(), resolvedArgs); + Object result = doInvoke(context, resolvedArgs); return ReactiveAdapterRegistryHelper.toFluxFromCollection(result); }); } - private Object[] getMethodArgumentValues(Collection keys, BatchLoaderEnvironment environment) { - Object[] args = new Object[getMethodParameters().length]; + private @Nullable Object[] getMethodArgumentValues(Collection keys, BatchLoaderEnvironment environment) { + @Nullable Object[] args = new Object[getMethodParameters().length]; for (int i = 0; i < getMethodParameters().length; i++) { args[i] = resolveArgument(getMethodParameters()[i], keys, environment); } return args; } - @Nullable - private Object resolveArgument( + private @Nullable Object resolveArgument( MethodParameter parameter, Collection keys, BatchLoaderEnvironment environment) { parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); @@ -169,8 +171,7 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport { } } - @Nullable - private Object resolveContextValueArgument(MethodParameter parameter, BatchLoaderEnvironment environment) { + private @Nullable Object resolveContextValueArgument(MethodParameter parameter, BatchLoaderEnvironment environment) { ContextValue annotation = parameter.getParameterAnnotation(ContextValue.class); Assert.state(annotation != null, "Expected @ContextValue annotation"); @@ -180,7 +181,7 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport { name, annotation.required(), parameter, environment.getContext()); } - private boolean doesNotHaveAsyncArgs(Object[] args) { + private boolean doesNotHaveAsyncArgs(@Nullable Object[] args) { return Arrays.stream(args).noneMatch((arg) -> arg instanceof Mono); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ContextValueMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ContextValueMethodArgumentResolver.java index e06c566e..977aca06 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ContextValueMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ContextValueMethodArgumentResolver.java @@ -21,12 +21,12 @@ import java.util.Optional; import graphql.GraphQLContext; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; import org.springframework.graphql.data.method.annotation.ContextValue; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -45,7 +45,7 @@ public class ContextValueMethodArgumentResolver implements HandlerMethodArgument } @Override - public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) { + public @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) { ContextValue annotation = parameter.getParameterAnnotation(ContextValue.class); Assert.state(annotation != null, "Expected @ContextValue annotation"); @@ -67,8 +67,7 @@ public class ContextValueMethodArgumentResolver implements HandlerMethodArgument "and parameter name information not found in class file either."); } - @Nullable - static Object resolveContextValue( + static @Nullable Object resolveContextValue( String contextValueName, boolean required, MethodParameter parameter, @Nullable GraphQLContext graphQlContext) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ContinuationHandlerMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ContinuationHandlerMethodArgumentResolver.java index 903e5666..a5091d51 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ContinuationHandlerMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ContinuationHandlerMethodArgumentResolver.java @@ -17,6 +17,7 @@ package org.springframework.graphql.data.method.annotation.support; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; @@ -35,7 +36,7 @@ public class ContinuationHandlerMethodArgumentResolver implements HandlerMethodA } @Override - public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) { + public @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) { return null; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java index ee830009..3185f7fc 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java @@ -22,13 +22,13 @@ import java.util.concurrent.Executor; import java.util.function.BiConsumer; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.graphql.data.method.HandlerMethod; import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; import org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite; import org.springframework.graphql.execution.ReactiveAdapterRegistryHelper; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -39,7 +39,7 @@ import org.springframework.util.Assert; */ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport { - private final BiConsumer validationHelper; + private final BiConsumer validationHelper; private final boolean subscription; @@ -56,7 +56,7 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport { @Deprecated(since = "1.3.0", forRemoval = true) public DataFetcherHandlerMethod( HandlerMethod handlerMethod, HandlerMethodArgumentResolverComposite resolvers, - @Nullable BiConsumer validationHelper, @Nullable Executor executor, + @Nullable BiConsumer validationHelper, @Nullable Executor executor, boolean subscription) { this(handlerMethod, resolvers, validationHelper, executor, subscription, false); @@ -74,12 +74,17 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport { */ public DataFetcherHandlerMethod( HandlerMethod handlerMethod, HandlerMethodArgumentResolverComposite resolvers, - @Nullable BiConsumer validationHelper, + @Nullable BiConsumer validationHelper, @Nullable Executor executor, boolean invokeAsync, boolean subscription) { super(handlerMethod, resolvers, executor, invokeAsync); Assert.isTrue(!resolvers.getResolvers().isEmpty(), "No argument resolvers"); - this.validationHelper = (validationHelper != null) ? validationHelper : (controller, args) -> { }; + this.validationHelper = (validationHelper != null) ? validationHelper : new BiConsumer() { + @Override + public void accept(Object o, @Nullable Object[] objects) { + + } + }; this.subscription = subscription; } @@ -98,8 +103,7 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport { * {@code Mono} in case a method argument requires asynchronous resolution; * {@code Mono} is returned if invocation fails. */ - @Nullable - public Object invoke(DataFetchingEnvironment environment) { + public @Nullable Object invoke(DataFetchingEnvironment environment) { return invoke(environment, new Object[0]); } @@ -111,9 +115,8 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport { * @since 1.2.0 */ @SuppressWarnings("ReactiveStreamsUnusedPublisher") - @Nullable - public Object invoke(DataFetchingEnvironment environment, Object... providedArgs) { - Object[] args; + public @Nullable Object invoke(DataFetchingEnvironment environment, Object... providedArgs) { + @Nullable Object[] args; try { args = getMethodArgumentValues(environment, providedArgs); } @@ -136,8 +139,7 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport { }); } - @Nullable - private Object validateAndInvoke(Object[] args, DataFetchingEnvironment environment) { + private @Nullable Object validateAndInvoke(@Nullable Object[] args, DataFetchingEnvironment environment) { this.validationHelper.accept(getBean(), args); return doInvoke(environment.getGraphQlContext(), args); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethodSupport.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethodSupport.java index fb8a42ff..fff900f0 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethodSupport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethodSupport.java @@ -19,6 +19,7 @@ package org.springframework.graphql.data.method.annotation.support; import java.util.concurrent.Executor; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; @@ -26,7 +27,6 @@ import org.springframework.core.ParameterNameDiscoverer; import org.springframework.graphql.data.method.HandlerMethod; import org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite; import org.springframework.graphql.data.method.InvocableHandlerMethodSupport; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; @@ -69,7 +69,7 @@ public class DataFetcherHandlerMethodSupport extends InvocableHandlerMethodSuppo * @param environment the data fetching environment to resolve arguments from * @param providedArgs the arguments provided directly */ - protected Object[] getMethodArgumentValues( + protected @Nullable Object[] getMethodArgumentValues( DataFetchingEnvironment environment, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); @@ -77,7 +77,7 @@ public class DataFetcherHandlerMethodSupport extends InvocableHandlerMethodSuppo return EMPTY_ARGS; } - Object[] args = new Object[parameters.length]; + @Nullable Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherMappingInfo.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherMappingInfo.java index 7b386f55..75e79539 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherMappingInfo.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherMappingInfo.java @@ -17,11 +17,11 @@ package org.springframework.graphql.data.method.annotation.support; import graphql.schema.FieldCoordinates; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.data.method.HandlerMethod; import org.springframework.graphql.data.method.annotation.BatchMapping; import org.springframework.graphql.data.method.annotation.SchemaMapping; -import org.springframework.lang.Nullable; /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetchingEnvironmentMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetchingEnvironmentMethodArgumentResolver.java index 1bef3e3a..bed0574b 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetchingEnvironmentMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetchingEnvironmentMethodArgumentResolver.java @@ -23,6 +23,7 @@ import graphql.GraphQLContext; import graphql.GraphqlErrorBuilder; import graphql.schema.DataFetchingEnvironment; import graphql.schema.DataFetchingFieldSelectionSet; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; @@ -55,7 +56,7 @@ public class DataFetchingEnvironmentMethodArgumentResolver implements HandlerMet } @Override - public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) { + public @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) { Class type = parameter.getParameterType(); if (type.equals(GraphQLContext.class)) { return environment.getGraphQlContext(); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataLoaderMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataLoaderMethodArgumentResolver.java index 02d2155e..d0355a75 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataLoaderMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataLoaderMethodArgumentResolver.java @@ -21,11 +21,11 @@ import java.lang.reflect.Type; import graphql.schema.DataFetchingEnvironment; import org.dataloader.DataLoader; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -73,8 +73,7 @@ public class DataLoaderMethodArgumentResolver implements HandlerMethodArgumentRe return dataLoader; } - @Nullable - private Class getValueType(MethodParameter param) { + private @Nullable Class getValueType(MethodParameter param) { Assert.isAssignable(DataLoader.class, param.getParameterType()); Type genericType = param.getGenericParameterType(); if (genericType instanceof ParameterizedType) { @@ -92,27 +91,36 @@ public class DataLoaderMethodArgumentResolver implements HandlerMethodArgumentRe MethodParameter parameter, DataFetchingEnvironment environment, @Nullable Class valueType, @Nullable String parameterName) { - String message = "Cannot resolve DataLoader for parameter" + - ((parameterName != null) ? " '" + parameterName + "'" : "[" + parameter.getParameterIndex() + "]") + - " in method " + parameter.getMethod().toGenericString() + ". "; + StringBuilder builder = new StringBuilder("Cannot resolve DataLoader for parameter"); - if (valueType == null) { - message += "If the batch loader was registered without a name, " + - "then declaring the DataLoader argument with generic types should help " + - "to look up the DataLoader based on the value type name."; - } - else if (parameterName == null) { - message += "If the batch loader was registered with a name, " + - "then compiling with \"-parameters\" should help " + - "to look up the DataLoader based on the parameter name."; + if (parameterName != null) { + builder.append(" '").append(parameterName).append("'"); } else { - message += "Neither the name of the declared value type '" + valueType + "' " + - "nor the method parameter name '" + parameterName + "' match to any DataLoader. " + - "The DataLoaderRegistry contains: " + environment.getDataLoaderRegistry().getKeys(); + builder.append("[").append(parameter.getParameterIndex()).append("]"); } + if (parameter.getMethod() != null) { + builder.append(" in method ").append(parameter.getMethod().toGenericString()); + } + builder.append(". "); - return message; + if (valueType == null) { + builder.append("If the batch loader was registered without a name, " + + "then declaring the DataLoader argument with generic types should help " + + "to look up the DataLoader based on the value type name."); + } + else if (parameterName == null) { + builder.append("If the batch loader was registered with a name, " + + "then compiling with \"-parameters\" should help " + + "to look up the DataLoader based on the parameter name."); + } + else { + builder.append("Neither the name of the declared value type '").append(valueType) + .append("' nor the method parameter name '").append(parameterName) + .append("' match to any DataLoader. The DataLoaderRegistry contains: ") + .append(environment.getDataLoaderRegistry().getKeys()); + } + return builder.toString(); } } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/HandlerDataFetcherExceptionResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/HandlerDataFetcherExceptionResolver.java index 7ade226f..6b6ff602 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/HandlerDataFetcherExceptionResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/HandlerDataFetcherExceptionResolver.java @@ -20,10 +20,10 @@ import java.util.List; import graphql.GraphQLError; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.graphql.execution.DataFetcherExceptionResolver; -import org.springframework.lang.Nullable; /** * Extension of {@link DataFetcherExceptionResolver} with overloaded method to diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/LocalContextValueMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/LocalContextValueMethodArgumentResolver.java index 5915bdc9..102aba89 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/LocalContextValueMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/LocalContextValueMethodArgumentResolver.java @@ -18,6 +18,7 @@ package org.springframework.graphql.data.method.annotation.support; import graphql.GraphQLContext; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; @@ -40,7 +41,7 @@ public class LocalContextValueMethodArgumentResolver implements HandlerMethodArg } @Override - public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) { + public @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) { LocalContextValue annotation = parameter.getParameterAnnotation(LocalContextValue.class); Assert.state(annotation != null, "Expected @LocalContextValue annotation"); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ProjectedPayloadMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ProjectedPayloadMethodArgumentResolver.java index 628f7a30..d493cf96 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ProjectedPayloadMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ProjectedPayloadMethodArgumentResolver.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Optional; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationContext; import org.springframework.core.MethodParameter; @@ -104,7 +105,7 @@ public class ProjectedPayloadMethodArgumentResolver implements HandlerMethodArgu } @Override - public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { + public @Nullable Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { String name = (parameter.hasParameterAnnotation(Argument.class) ? ArgumentMethodArgumentResolver.getArgumentName(parameter) : null); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingBeanFactoryInitializationAotProcessor.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingBeanFactoryInitializationAotProcessor.java index da47ab22..5310a97d 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingBeanFactoryInitializationAotProcessor.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingBeanFactoryInitializationAotProcessor.java @@ -139,7 +139,7 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI RuntimeHints runtimeHints = context.getRuntimeHints(); registerSpringDataSpelSupport(runtimeHints); this.controllers.forEach((controller) -> { - runtimeHints.reflection().registerType(controller, MemberCategory.INTROSPECT_DECLARED_METHODS); + runtimeHints.reflection().registerType(controller); ReflectionUtils.doWithMethods(controller, (method) -> processSchemaMappingMethod(runtimeHints, method), this::isGraphQlHandlerMethod); @@ -148,7 +148,7 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI this::isExceptionHandlerMethod); }); this.controllerAdvices.forEach((controllerAdvice) -> { - runtimeHints.reflection().registerType(controllerAdvice, MemberCategory.INTROSPECT_DECLARED_METHODS); + runtimeHints.reflection().registerType(controllerAdvice); ReflectionUtils.doWithMethods(controllerAdvice, (method) -> processExceptionHandlerMethod(runtimeHints, method), this::isExceptionHandlerMethod); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ScrollSubrangeMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ScrollSubrangeMethodArgumentResolver.java index fbc259b7..05d1054c 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ScrollSubrangeMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ScrollSubrangeMethodArgumentResolver.java @@ -17,11 +17,12 @@ package org.springframework.graphql.data.method.annotation.support; +import org.jspecify.annotations.Nullable; + import org.springframework.core.MethodParameter; import org.springframework.data.domain.ScrollPosition; import org.springframework.graphql.data.pagination.CursorStrategy; import org.springframework.graphql.data.query.ScrollSubrange; -import org.springframework.lang.Nullable; /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SubrangeMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SubrangeMethodArgumentResolver.java index 949b39b9..f6aa41c6 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SubrangeMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SubrangeMethodArgumentResolver.java @@ -18,12 +18,12 @@ package org.springframework.graphql.data.method.annotation.support; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; import org.springframework.graphql.data.pagination.CursorStrategy; import org.springframework.graphql.data.pagination.Subrange; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ValidationHelper.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ValidationHelper.java index 778eb614..01676249 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ValidationHelper.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ValidationHelper.java @@ -26,13 +26,13 @@ import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; import jakarta.validation.Valid; import jakarta.validation.Validator; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationContext; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.graphql.data.method.HandlerMethod; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.validation.annotation.Validated; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; @@ -61,8 +61,7 @@ final class ValidationHelper { * possibly {@code null} if the method or the method parameters do not have * {@link Validated}, {@link Valid}, or {@link Constraint} annotations. */ - @Nullable - BiConsumer getValidationHelperFor(HandlerMethod handlerMethod) { + @Nullable BiConsumer getValidationHelperFor(HandlerMethod handlerMethod) { boolean requiresMethodValidation = false; Class[] methodValidationGroups = null; @@ -76,7 +75,7 @@ final class ValidationHelper { requiresMethodValidation = true; } - BiConsumer parameterValidator = null; + BiConsumer parameterValidator = null; MethodParameter[] parameters = handlerMethod.getMethodParameters(); for (int i = 0; i < parameters.length; i++) { @@ -95,7 +94,7 @@ final class ValidationHelper { } } - BiConsumer result = (requiresMethodValidation) ? + BiConsumer result = (requiresMethodValidation) ? new HandlerMethodValidator(handlerMethod, methodValidationGroups) : null; if (parameterValidator != null) { @@ -105,8 +104,7 @@ final class ValidationHelper { return result; } - @Nullable - private A findAnnotation(HandlerMethod method, Class annotationType) { + private @Nullable A findAnnotation(HandlerMethod method, Class annotationType) { A annotation = AnnotationUtils.findAnnotation(method.getMethod(), annotationType); if (annotation == null) { annotation = AnnotationUtils.findAnnotation(method.getBeanType(), annotationType); @@ -119,8 +117,7 @@ final class ValidationHelper { * Factory method to create a {@link ValidationHelper} if there is a * {@link Validator} bean declared, or {@code null} otherwise. */ - @Nullable - static ValidationHelper createIfValidatorPresent(ApplicationContext context) { + static @Nullable ValidationHelper createIfValidatorPresent(ApplicationContext context) { Validator validator = context.getBeanProvider(Validator.class).getIfAvailable(); if (validator instanceof LocalValidatorFactoryBean) { validator = ((LocalValidatorFactoryBean) validator).getValidator(); @@ -142,20 +139,20 @@ final class ValidationHelper { /** * Callback to apply validation to the invocation of a {@link HandlerMethod}. */ - private class HandlerMethodValidator implements BiConsumer { + private class HandlerMethodValidator implements BiConsumer { private final Method method; private final Class[] validationGroups; - HandlerMethodValidator(HandlerMethod handlerMethod, @Nullable Class[] validationGroups) { + HandlerMethodValidator(HandlerMethod handlerMethod, Class @Nullable[] validationGroups) { Assert.notNull(handlerMethod, "HandlerMethod is required"); this.method = handlerMethod.getMethod(); this.validationGroups = (validationGroups != null) ? validationGroups : new Class[] {}; } @Override - public void accept(Object controller, Object[] arguments) { + public void accept(Object controller, @Nullable Object[] arguments) { Set> violations = ValidationHelper.this.validator.forExecutables() @@ -173,25 +170,27 @@ final class ValidationHelper { * because it's annotated with Spring's {@code @Validated} rather than with * {@code @Valid}. */ - private class MethodParameterValidator implements BiConsumer { + private class MethodParameterValidator implements BiConsumer { private final int index; private final Class[] validationGroups; - MethodParameterValidator(int index, @Nullable Class[] validationGroups) { + MethodParameterValidator(int index, Class @Nullable[] validationGroups) { this.index = index; this.validationGroups = (validationGroups != null) ? validationGroups : new Class[] {}; } @Override - public void accept(Object controller, Object[] arguments) { + public void accept(Object controller, @Nullable Object[] arguments) { + Object argument = arguments[this.index]; + if (argument != null) { + Set> violations = + ValidationHelper.this.validator.validate(argument, this.validationGroups); - Set> violations = - ValidationHelper.this.validator.validate(arguments[this.index], this.validationGroups); - - if (!violations.isEmpty()) { - throw new ConstraintViolationException(violations); + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } } } } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/package-info.java index fea82553..a623e4dd 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 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. @@ -18,9 +18,7 @@ * Resolvers for method parameters of annotated handler methods. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.data.method.annotation.support; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/package-info.java index 4e0ee83c..d5a0602b 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -19,9 +19,7 @@ * {@link org.springframework.graphql.data.method.annotation.SchemaMapping} * annotations. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.data.method; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/data/package-info.java index e4de0a1a..cc254820 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -17,9 +17,7 @@ /** * Support for various ways to implement {@link graphql.schema.DataFetcher}s. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.data; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/CompositeConnectionAdapter.java b/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/CompositeConnectionAdapter.java index 4c79a3b8..3c9927ba 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/CompositeConnectionAdapter.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/CompositeConnectionAdapter.java @@ -19,7 +19,8 @@ package org.springframework.graphql.data.pagination; import java.util.Collection; import java.util.List; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -70,8 +71,7 @@ final class CompositeConnectionAdapter implements ConnectionAdapter { return adapter; } - @Nullable - private ConnectionAdapter getAdapter(Class containerType) { + private @Nullable ConnectionAdapter getAdapter(Class containerType) { for (ConnectionAdapter adapter : this.adapters) { if (adapter.supports(containerType)) { return adapter; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/ConnectionFieldTypeVisitor.java b/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/ConnectionFieldTypeVisitor.java index c610b9f1..0799bacb 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/ConnectionFieldTypeVisitor.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/ConnectionFieldTypeVisitor.java @@ -46,10 +46,10 @@ import graphql.util.TraversalControl; import graphql.util.TraverserContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.graphql.execution.TypeVisitorHelper; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -144,13 +144,11 @@ public final class ConnectionFieldTypeVisitor extends GraphQLTypeVisitorStub { return true; } - @Nullable - private static GraphQLObjectType getAsObjectType(@Nullable GraphQLFieldDefinition field) { + private static @Nullable GraphQLObjectType getAsObjectType(@Nullable GraphQLFieldDefinition field) { return (getType(field) instanceof GraphQLObjectType type) ? type : null; } - @Nullable - private static GraphQLObjectType getEdgeType(@Nullable GraphQLFieldDefinition field) { + private static @Nullable GraphQLObjectType getEdgeType(@Nullable GraphQLFieldDefinition field) { if (getType(field) instanceof GraphQLList listType) { if (unwrapNonNullType(listType.getWrappedType()) instanceof GraphQLObjectType type) { return type; @@ -159,16 +157,14 @@ public final class ConnectionFieldTypeVisitor extends GraphQLTypeVisitorStub { return null; } - @Nullable - private static GraphQLType getType(@Nullable GraphQLFieldDefinition field) { + private static @Nullable GraphQLType getType(@Nullable GraphQLFieldDefinition field) { if (field == null) { return null; } return unwrapNonNullType(field.getType()); } - @Nullable - private static GraphQLType unwrapNonNullType(@Nullable GraphQLType type) { + private static @Nullable GraphQLType unwrapNonNullType(@Nullable GraphQLType type) { if (type == null) { return null; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/Subrange.java b/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/Subrange.java index bf390fd0..51d3fbe9 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/Subrange.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/Subrange.java @@ -20,7 +20,7 @@ package org.springframework.graphql.data.pagination; import java.util.Optional; import java.util.OptionalInt; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Container for parameters that limit result elements to a subrange including a diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/package-info.java index f2bb459f..2e91cccd 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/pagination/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -17,9 +17,7 @@ /** * Core contracts and generic infrastructure classes for pagination. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.data.pagination; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/query/AbstractSortStrategy.java b/spring-graphql/src/main/java/org/springframework/graphql/data/query/AbstractSortStrategy.java index ad182c30..e29b0fb1 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/query/AbstractSortStrategy.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/query/AbstractSortStrategy.java @@ -21,9 +21,9 @@ import java.util.ArrayList; import java.util.List; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Sort; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** @@ -48,7 +48,7 @@ public abstract class AbstractSortStrategy implements SortStrategy { } return Sort.by(sortOrders); } - return null; + return Sort.unsorted(); } /** @@ -61,7 +61,6 @@ public abstract class AbstractSortStrategy implements SortStrategy { * Return the sort direction to use, or {@code null}. * @param environment the data fetching environment for this operation */ - @Nullable - protected abstract Sort.Direction getDirection(DataFetchingEnvironment environment); + protected abstract Sort.@Nullable Direction getDirection(DataFetchingEnvironment environment); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/query/AutoRegistrationRuntimeWiringConfigurer.java b/spring-graphql/src/main/java/org/springframework/graphql/data/query/AutoRegistrationRuntimeWiringConfigurer.java index 8e48b09a..924d66b4 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/query/AutoRegistrationRuntimeWiringConfigurer.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/query/AutoRegistrationRuntimeWiringConfigurer.java @@ -32,9 +32,9 @@ import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.WiringFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.execution.RuntimeWiringConfigurer; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -98,8 +98,7 @@ class AutoRegistrationRuntimeWiringConfigurer implements RuntimeWiringConfigurer private final RuntimeWiring.Builder builder; - @Nullable - private Predicate existingQueryDataFetcherPredicate; + private @Nullable Predicate existingQueryDataFetcherPredicate; AutoRegistrationWiringFactory(RuntimeWiring.Builder builder) { this.builder = builder; @@ -129,8 +128,7 @@ class AutoRegistrationRuntimeWiringConfigurer implements RuntimeWiringConfigurer return result; } - @Nullable - private String getOutputTypeName(FieldWiringEnvironment environment) { + private @Nullable String getOutputTypeName(FieldWiringEnvironment environment) { GraphQLType outputType = removeNonNullWrapper(environment.getFieldType()); if (isConnectionType(outputType)) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/query/QueryByExampleDataFetcher.java b/spring-graphql/src/main/java/org/springframework/graphql/data/query/QueryByExampleDataFetcher.java index 434cfce5..cfd15f95 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/query/QueryByExampleDataFetcher.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/query/QueryByExampleDataFetcher.java @@ -29,6 +29,7 @@ import graphql.schema.DataFetchingFieldSelectionSet; import graphql.schema.GraphQLArgument; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -50,7 +51,6 @@ import org.springframework.graphql.data.pagination.CursorStrategy; import org.springframework.graphql.data.query.AutoRegistrationRuntimeWiringConfigurer.DataFetcherFactory; import org.springframework.graphql.execution.RuntimeWiringConfigurer; import org.springframework.graphql.execution.SelfDescribingDataFetcher; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.validation.BindException; @@ -128,7 +128,7 @@ public abstract class QueryByExampleDataFetcher { * @param environment contextual info for the GraphQL request * @return the resulting example */ - @SuppressWarnings({"ConstantConditions", "unchecked"}) + @SuppressWarnings({"ConstantConditions", "unchecked", "NullAway"}) protected Example buildExample(DataFetchingEnvironment environment) throws BindException { String name = getArgumentName(environment); ResolvableType targetType = ResolvableType.forClass(this.domainType.getType()); @@ -140,8 +140,7 @@ public abstract class QueryByExampleDataFetcher { * name, thereby nesting and having the example Object populated from the * sub-map. Otherwise, {@code null} to bind using the top-level map. */ - @Nullable - private static String getArgumentName(DataFetchingEnvironment environment) { + private static @Nullable String getArgumentName(DataFetchingEnvironment environment) { Map arguments = environment.getArguments(); List definedArguments = environment.getFieldDefinition().getArguments(); if (definedArguments.size() == 1) { @@ -328,14 +327,11 @@ public abstract class QueryByExampleDataFetcher { private final Class resultType; - @Nullable - private final CursorStrategy cursorStrategy; + private final @Nullable CursorStrategy cursorStrategy; - @Nullable - private final Integer defaultScrollCount; + private final @Nullable Integer defaultScrollCount; - @Nullable - private final Function defaultScrollPosition; + private final @Nullable Function defaultScrollPosition; private final Sort sort; @@ -507,14 +503,11 @@ public abstract class QueryByExampleDataFetcher { private final Class resultType; - @Nullable - private final CursorStrategy cursorStrategy; + private final @Nullable CursorStrategy cursorStrategy; - @Nullable - private final Integer defaultScrollCount; + private final @Nullable Integer defaultScrollCount; - @Nullable - private final Function defaultScrollPosition; + private final @Nullable Function defaultScrollPosition; private final Sort sort; @@ -695,7 +688,7 @@ public abstract class QueryByExampleDataFetcher { @Override @SuppressWarnings({"ConstantConditions", "unchecked"}) - public R get(DataFetchingEnvironment env) throws BindException { + public @Nullable R get(DataFetchingEnvironment env) throws BindException { return this.executor.findBy(buildExample(env), (query) -> { FluentQuery.FetchableFluentQuery queryToUse = (FluentQuery.FetchableFluentQuery) query; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/query/QuerydslDataFetcher.java b/spring-graphql/src/main/java/org/springframework/graphql/data/query/QuerydslDataFetcher.java index 11a7c41e..e3cc64aa 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/query/QuerydslDataFetcher.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/query/QuerydslDataFetcher.java @@ -30,6 +30,7 @@ import graphql.schema.DataFetchingEnvironment; import graphql.schema.DataFetchingFieldSelectionSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -54,7 +55,6 @@ import org.springframework.graphql.data.pagination.CursorStrategy; import org.springframework.graphql.data.query.AutoRegistrationRuntimeWiringConfigurer.DataFetcherFactory; import org.springframework.graphql.execution.RuntimeWiringConfigurer; import org.springframework.graphql.execution.SelfDescribingDataFetcher; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -374,14 +374,11 @@ public abstract class QuerydslDataFetcher { private final Class resultType; - @Nullable - private final CursorStrategy cursorStrategy; + private final @Nullable CursorStrategy cursorStrategy; - @Nullable - private final Integer defaultScrollCount; + private final @Nullable Integer defaultScrollCount; - @Nullable - private final Function defaultScrollPosition; + private final @Nullable Function defaultScrollPosition; private final Sort sort; @@ -579,14 +576,11 @@ public abstract class QuerydslDataFetcher { private final Class resultType; - @Nullable - private final CursorStrategy cursorStrategy; + private final @Nullable CursorStrategy cursorStrategy; - @Nullable - private final Integer defaultScrollCount; + private final @Nullable Integer defaultScrollCount; - @Nullable - private final Function defaultScrollPosition; + private final @Nullable Function defaultScrollPosition; private final Sort sort; @@ -797,7 +791,7 @@ public abstract class QuerydslDataFetcher { @Override @SuppressWarnings({"ConstantConditions", "unchecked"}) - public R get(DataFetchingEnvironment env) { + public @Nullable R get(DataFetchingEnvironment env) { return this.executor.findBy(buildPredicate(env), (query) -> { FetchableFluentQuery queryToUse = (FetchableFluentQuery) query; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/query/RepositoryUtils.java b/spring-graphql/src/main/java/org/springframework/graphql/data/query/RepositoryUtils.java index b99c2434..fc0aa8d4 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/query/RepositoryUtils.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/query/RepositoryUtils.java @@ -20,6 +20,7 @@ import java.lang.reflect.Type; import java.util.function.Function; import graphql.schema.DataFetchingEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; @@ -32,7 +33,6 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat import org.springframework.graphql.data.GraphQlRepository; import org.springframework.graphql.data.pagination.CursorEncoder; import org.springframework.graphql.data.pagination.CursorStrategy; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -71,8 +71,7 @@ final class RepositoryUtils { String.format("Cannot resolve repository interface from %s", executor)); } - @Nullable - static String getGraphQlTypeName(Object repository) { + static @Nullable String getGraphQlTypeName(Object repository) { GraphQlRepository annotation = AnnotatedElementUtils.findMergedAnnotation(repository.getClass(), GraphQlRepository.class); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/query/ScrollSubrange.java b/spring-graphql/src/main/java/org/springframework/graphql/data/query/ScrollSubrange.java index 595f27ac..cb39a878 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/query/ScrollSubrange.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/query/ScrollSubrange.java @@ -17,11 +17,12 @@ package org.springframework.graphql.data.query; +import org.jspecify.annotations.Nullable; + import org.springframework.data.domain.KeysetScrollPosition; import org.springframework.data.domain.OffsetScrollPosition; import org.springframework.data.domain.ScrollPosition; import org.springframework.graphql.data.pagination.Subrange; -import org.springframework.lang.Nullable; /** * {@link Subrange} implementation for a {@link ScrollPosition} cursor. diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/query/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/data/query/package-info.java index eb77d17b..c62df879 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/query/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/query/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -23,9 +23,7 @@ * @see * Spring Data Querydsl */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.data.query; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/AbstractGraphQlSourceBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/AbstractGraphQlSourceBuilder.java index 09b6af76..e9c80c3d 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/AbstractGraphQlSourceBuilder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/AbstractGraphQlSourceBuilder.java @@ -30,8 +30,8 @@ import graphql.schema.GraphQLSchema; import graphql.schema.GraphQLTypeVisitor; import graphql.schema.SchemaTransformer; import graphql.schema.SchemaTraverser; +import org.jspecify.annotations.Nullable; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -56,8 +56,7 @@ public abstract class AbstractGraphQlSourceBuilder instrumentations = new ArrayList<>(); - @Nullable - private Consumer graphQlConfigurer; + private @Nullable Consumer graphQlConfigurer; private GraphQlSource.Factory graphQlSourceFactory = FixedGraphQlSource::new; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ClassNameTypeResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ClassNameTypeResolver.java index 8832f475..3c2f4c33 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ClassNameTypeResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ClassNameTypeResolver.java @@ -25,8 +25,8 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.GraphQLType; import graphql.schema.TypeResolver; +import org.jspecify.annotations.Nullable; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -76,8 +76,7 @@ public class ClassNameTypeResolver implements TypeResolver { } @Override - @Nullable - public GraphQLObjectType getType(TypeResolutionEnvironment environment) { + public @Nullable GraphQLObjectType getType(TypeResolutionEnvironment environment) { Class clazz = environment.getObject().getClass(); GraphQLSchema schema = environment.getSchema(); @@ -88,8 +87,7 @@ public class ClassNameTypeResolver implements TypeResolver { return getTypeForClass(clazz, schema); } - @Nullable - private GraphQLObjectType getTypeForClass(Class clazz, GraphQLSchema schema) { + private @Nullable GraphQLObjectType getTypeForClass(Class clazz, GraphQLSchema schema) { if (clazz.getName().startsWith("java.")) { return null; } @@ -126,8 +124,7 @@ public class ClassNameTypeResolver implements TypeResolver { return null; } - @Nullable - private String getMapping(Class targetClass) { + private @Nullable String getMapping(Class targetClass) { for (Map.Entry, String> entry : this.mappings.entrySet()) { if (entry.getKey().isAssignableFrom(targetClass)) { return entry.getValue(); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextDataFetcherDecorator.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextDataFetcherDecorator.java index 3b0c0535..18aa5bed 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextDataFetcherDecorator.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextDataFetcherDecorator.java @@ -37,12 +37,12 @@ import graphql.util.TraversalControl; import graphql.util.TraverserContext; import io.micrometer.context.ContextSnapshot; import io.micrometer.context.ContextSnapshotFactory; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -81,7 +81,7 @@ class ContextDataFetcherDecorator implements DataFetcher { @Override - public Object get(DataFetchingEnvironment env) throws Exception { + public @Nullable Object get(DataFetchingEnvironment env) throws Exception { GraphQLContext graphQlContext = env.getGraphQlContext(); ContextSnapshotFactory snapshotFactory = ContextPropagationHelper.getInstance(graphQlContext); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextPropagationHelper.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextPropagationHelper.java index 7a45d7ff..4e408881 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextPropagationHelper.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextPropagationHelper.java @@ -19,14 +19,13 @@ package org.springframework.graphql.execution; import graphql.GraphQLContext; import io.micrometer.context.ContextSnapshot; import io.micrometer.context.ContextSnapshotFactory; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; import reactor.util.context.Context; import reactor.util.context.ContextView; -import org.springframework.lang.Nullable; - /** * Helper for propagating context values from and to Reactor and GraphQL contexts. * diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/DataFetcherExceptionResolverAdapter.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/DataFetcherExceptionResolverAdapter.java index 0956ad83..41d83a1c 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/DataFetcherExceptionResolverAdapter.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/DataFetcherExceptionResolverAdapter.java @@ -25,10 +25,9 @@ import graphql.schema.DataFetchingEnvironment; import io.micrometer.context.ThreadLocalAccessor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; -import org.springframework.lang.Nullable; - /** * Adapter for {@link DataFetcherExceptionResolver} that pre-implements the * asynchronous contract and exposes the following synchronous protected methods: @@ -92,8 +91,7 @@ public abstract class DataFetcherExceptionResolverAdapter implements DataFetcher return Mono.defer(() -> Mono.justOrEmpty(resolveInternal(ex, env))); } - @Nullable - private List resolveInternal(Throwable exception, DataFetchingEnvironment env) { + private @Nullable List resolveInternal(Throwable exception, DataFetchingEnvironment env) { try { return (this.threadLocalContextAware) ? ContextPropagationHelper.captureFrom(env.getGraphQlContext()) @@ -113,8 +111,7 @@ public abstract class DataFetcherExceptionResolverAdapter implements DataFetcher * @param env the environment for the invoked {@code DataFetcher} * @return the resolved errors or {@code null} if unresolved */ - @Nullable - protected List resolveToMultipleErrors(Throwable ex, DataFetchingEnvironment env) { + protected @Nullable List resolveToMultipleErrors(Throwable ex, DataFetchingEnvironment env) { GraphQLError error = resolveToSingleError(ex, env); return (error != null) ? Collections.singletonList(error) : null; } @@ -125,8 +122,7 @@ public abstract class DataFetcherExceptionResolverAdapter implements DataFetcher * @param env the environment for the invoked {@code DataFetcher} * @return the resolved error or {@code null} if unresolved */ - @Nullable - protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) { + protected @Nullable GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) { return null; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java index d779e6cf..ec9602d0 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java @@ -36,10 +36,10 @@ import org.dataloader.DataLoaderFactory; import org.dataloader.DataLoaderOptions; import org.dataloader.DataLoaderRegistry; import org.dataloader.MappedBatchLoaderWithContext; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -111,6 +111,7 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { } } + @SuppressWarnings("NullAway") // DataLoaderRegistry#getDataLoader should be @Nullable private void registerDataLoader(DataLoader dataLoader, DataLoaderRegistry registry) { if (registry.getDataLoader(dataLoader.getName()) != null) { throw new IllegalStateException("More than one DataLoader named '" + dataLoader.getName() + "'"); @@ -121,17 +122,13 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { private class DefaultRegistrationSpec implements RegistrationSpec { - @Nullable - private final Class valueType; + private final @Nullable Class valueType; - @Nullable - private String name; + private @Nullable String name; - @Nullable - private DataLoaderOptions options; + private @Nullable DataLoaderOptions options; - @Nullable - private Consumer optionsBuilderConsumer; + private @Nullable Consumer optionsBuilderConsumer; DefaultRegistrationSpec(Class valueType) { this.valueType = valueType; @@ -232,6 +229,7 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { @Override public CompletionStage> load(List keys, BatchLoaderEnvironment environment) { GraphQLContext graphQLContext = environment.getContext(); + Assert.state(graphQLContext != null, "No GraphQLContext available"); ContextSnapshot snapshot = ContextPropagationHelper.captureFrom(graphQLContext); try { return snapshot.wrap(() -> @@ -280,6 +278,7 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { @Override public CompletionStage> load(Set keys, BatchLoaderEnvironment environment) { GraphQLContext graphQLContext = environment.getContext(); + Assert.state(graphQLContext != null, "No GraphQLContext available"); ContextSnapshot snapshot = ContextPropagationHelper.captureFrom(graphQLContext); try { return snapshot.wrap(() -> diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultExecutionGraphQlService.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultExecutionGraphQlService.java index b1343cb1..ba95c81f 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultExecutionGraphQlService.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultExecutionGraphQlService.java @@ -29,6 +29,7 @@ import graphql.execution.ExecutionIdProvider; import graphql.execution.instrumentation.dataloader.EmptyDataLoaderRegistryInstance; import io.micrometer.context.ContextSnapshotFactory; import org.dataloader.DataLoaderRegistry; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; @@ -36,7 +37,6 @@ import org.springframework.graphql.ExecutionGraphQlRequest; import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.ExecutionGraphQlService; import org.springframework.graphql.support.DefaultExecutionGraphQlResponse; -import org.springframework.lang.Nullable; /** * {@link ExecutionGraphQlService} that uses a {@link GraphQlSource} to obtain a @@ -55,8 +55,7 @@ public class DefaultExecutionGraphQlService implements ExecutionGraphQlService { private final List dataLoaderRegistrars = new ArrayList<>(); - @Nullable - private Boolean hasDataLoaderRegistrations; + private @Nullable Boolean hasDataLoaderRegistrations; private final boolean isDefaultExecutionIdProvider; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java index be163f48..abc190d5 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java @@ -43,9 +43,9 @@ import graphql.schema.idl.TypeDefinitionRegistry; import graphql.schema.idl.WiringFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.core.io.Resource; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -70,19 +70,15 @@ final class DefaultSchemaResourceGraphQlSourceBuilder private final List runtimeWiringConfigurers = new ArrayList<>(); - @Nullable - private TypeResolver typeResolver; + private @Nullable TypeResolver typeResolver; - @Nullable - private BiFunction schemaFactory; + private @Nullable BiFunction schemaFactory; - @Nullable - private Consumer schemaReportConsumer; + private @Nullable Consumer schemaReportConsumer; private Consumer inspectorInitializerConsumer = (initializer) -> { }; - @Nullable - private Consumer schemaReportRunner; + private @Nullable Consumer schemaReportRunner; @Override @@ -133,6 +129,7 @@ final class DefaultSchemaResourceGraphQlSourceBuilder } @Override + @SuppressWarnings("NullAway") protected GraphQLSchema initGraphQlSchema() { TypeDefinitionRegistry registry = this.schemaResources.stream() @@ -166,8 +163,7 @@ final class DefaultSchemaResourceGraphQlSourceBuilder // visitors may transform the schema, for example to add Connection types. if (this.schemaReportConsumer != null) { - this.schemaReportRunner = (schema) -> - this.schemaReportConsumer.accept(createSchemaReport(schema, runtimeWiring)); + this.schemaReportRunner = (schema) -> this.schemaReportConsumer.accept(createSchemaReport(schema, runtimeWiring)); } return (this.schemaFactory != null) ? diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java index d2913c08..f3898ee5 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java @@ -88,7 +88,10 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler private Throwable unwrapException(DataFetcherExceptionHandlerParameters params) { Throwable ex = params.getException(); - return ((ex instanceof CompletionException) ? ex.getCause() : ex); + if (ex instanceof CompletionException completionException) { + return (completionException.getCause() != null) ? completionException.getCause() : completionException; + } + return ex; } private void logResolvedException(Throwable ex, DataFetcherExceptionHandlerResult result) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactiveAdapterRegistryHelper.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactiveAdapterRegistryHelper.java index 4368f566..6f10a11e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactiveAdapterRegistryHelper.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactiveAdapterRegistryHelper.java @@ -18,7 +18,7 @@ package org.springframework.graphql.execution; import java.util.Collection; -import io.micrometer.context.Nullable; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -60,8 +60,7 @@ public abstract class ReactiveAdapterRegistryHelper { * @return the same instance or a {@code Mono} if the object is known to * {@code ReactiveAdapterRegistry} */ - @Nullable - public static Object toMonoIfReactive(@Nullable Object result) { + public static @Nullable Object toMonoIfReactive(@Nullable Object result) { ReactiveAdapter adapter = ((result != null) ? registry.getAdapter(result.getClass()) : null); if (adapter == null) { return result; @@ -77,8 +76,7 @@ public abstract class ReactiveAdapterRegistryHelper { * @param result the result Object to adapt * @return the same instance, a {@code Mono}, or a {@code Flux} */ - @Nullable - public static Object toMonoOrFluxIfReactive(@Nullable Object result) { + public static @Nullable Object toMonoOrFluxIfReactive(@Nullable Object result) { ReactiveAdapter adapter = ((result != null) ? registry.getAdapter(result.getClass()) : null); if (adapter == null) { return result; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java index 99d53c22..50b6b014 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java @@ -49,6 +49,7 @@ import graphql.schema.GraphQLUnionType; import graphql.schema.idl.RuntimeWiring; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; @@ -56,7 +57,6 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ReactiveAdapter; import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; @@ -104,8 +104,7 @@ public final class SchemaMappingInspector { private final ReportBuilder reportBuilder = new ReportBuilder(); - @Nullable - private SchemaReport report; + private @Nullable SchemaReport report; private SchemaMappingInspector( @@ -262,8 +261,7 @@ public final class SchemaMappingInspector { } } - @Nullable - private PropertyDescriptor getProperty(ResolvableType resolvableType, String fieldName) { + private @Nullable PropertyDescriptor getProperty(ResolvableType resolvableType, String fieldName) { try { Class clazz = resolvableType.resolve(); return (clazz != null) ? BeanUtils.getPropertyDescriptor(clazz, fieldName) : null; @@ -274,8 +272,7 @@ public final class SchemaMappingInspector { } } - @Nullable - private Field getField(ResolvableType resolvableType, String fieldName) { + private @Nullable Field getField(ResolvableType resolvableType, String fieldName) { try { Class clazz = resolvableType.resolve(); return (clazz != null) ? clazz.getField(fieldName) : null; @@ -285,8 +282,7 @@ public final class SchemaMappingInspector { } } - @Nullable - private static Method getRecordLikeMethod(ResolvableType resolvableType, String fieldName) { + private static @Nullable Method getRecordLikeMethod(ResolvableType resolvableType, String fieldName) { Class clazz = resolvableType.resolve(); if (clazz != null) { for (Method method : clazz.getDeclaredMethods()) { @@ -721,8 +717,7 @@ public final class SchemaMappingInspector { return (type instanceof GraphQLNonNull graphQLNonNull) ? graphQLNonNull.getWrappedType() : type; } - @Nullable - private static GraphQLType getPaginatedType(GraphQLType type) { + private static @Nullable GraphQLType getPaginatedType(GraphQLType type) { if (!(type instanceof GraphQLObjectType cot && cot.getName().endsWith("Connection"))) { return null; } @@ -912,8 +907,7 @@ public final class SchemaMappingInspector { } @Override - @Nullable - public DataFetcher dataFetcher(FieldCoordinates coordinates) { + public @Nullable DataFetcher dataFetcher(FieldCoordinates coordinates) { return SchemaMappingInspector.this.dataFetchers .getOrDefault(coordinates.getTypeName(), Collections.emptyMap()) .get(coordinates.getFieldName()); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaReport.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaReport.java index 7860a7c0..bec3fbe0 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaReport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaReport.java @@ -25,8 +25,8 @@ import graphql.schema.DataFetcher; import graphql.schema.FieldCoordinates; import graphql.schema.GraphQLSchema; import graphql.schema.GraphQLType; +import org.jspecify.annotations.Nullable; -import org.springframework.lang.Nullable; import org.springframework.util.MultiValueMap; /** @@ -81,8 +81,7 @@ public interface SchemaReport { * Return the {@code DataFetcher} for the given field coordinates, if registered. * @param coordinates the field coordinates */ - @Nullable - DataFetcher dataFetcher(FieldCoordinates coordinates); + @Nullable DataFetcher dataFetcher(FieldCoordinates coordinates); /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/SecurityContextThreadLocalAccessor.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/SecurityContextThreadLocalAccessor.java index 81ff664d..baa445af 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/SecurityContextThreadLocalAccessor.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/SecurityContextThreadLocalAccessor.java @@ -17,6 +17,7 @@ package org.springframework.graphql.execution; import io.micrometer.context.ThreadLocalAccessor; +import org.jspecify.annotations.Nullable; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -58,7 +59,7 @@ public class SecurityContextThreadLocalAccessor implements ThreadLocalAccessor resolveToMultipleErrors(Throwable exception) { + protected @Nullable List resolveToMultipleErrors(Throwable exception) { GraphQLError error = resolveToSingleError(exception); return (error != null) ? Collections.singletonList(error) : null; } @@ -115,8 +113,7 @@ public abstract class SubscriptionExceptionResolverAdapter implements Subscripti * @param exception the exception to resolve * @return the resolved error or {@code null} if unresolved */ - @Nullable - protected GraphQLError resolveToSingleError(Throwable exception) { + protected @Nullable GraphQLError resolveToSingleError(Throwable exception) { return null; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/SubscriptionPublisherException.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/SubscriptionPublisherException.java index 75a40b3d..2c136224 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/SubscriptionPublisherException.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/SubscriptionPublisherException.java @@ -21,9 +21,9 @@ import java.util.Map; import graphql.ExecutionResult; import graphql.GraphQLError; +import org.jspecify.annotations.Nullable; import org.springframework.core.NestedRuntimeException; -import org.springframework.lang.Nullable; /** * An exception raised after a GraphQL subscription diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/package-info.java index f4eb7f0d..0dc616fb 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -18,9 +18,7 @@ * Support for GraphQL request execution, including abstractions to configure and invoke * {@link graphql.GraphQL}. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.execution; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/observation/DataFetcherObservationContext.java b/spring-graphql/src/main/java/org/springframework/graphql/observation/DataFetcherObservationContext.java index d32b5aff..f924de68 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/observation/DataFetcherObservationContext.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/observation/DataFetcherObservationContext.java @@ -18,6 +18,7 @@ package org.springframework.graphql.observation; import graphql.schema.DataFetchingEnvironment; import io.micrometer.observation.Observation; +import org.jspecify.annotations.Nullable; /** * Context that holds information for metadata collection during observations @@ -30,7 +31,7 @@ public class DataFetcherObservationContext extends Observation.Context { private final DataFetchingEnvironment environment; - private Object value; + private @Nullable Object value; DataFetcherObservationContext(DataFetchingEnvironment environment) { this.environment = environment; @@ -47,11 +48,11 @@ public class DataFetcherObservationContext extends Observation.Context { * Return the value returned by the {@link graphql.schema.DataFetcher}, if any. * @see #getError() for the exception thrown by the data fetcher. */ - public Object getValue() { + public @Nullable Object getValue() { return this.value; } - void setValue(Object value) { + void setValue(@Nullable Object value) { this.value = value; } } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/observation/ExecutionRequestObservationContext.java b/spring-graphql/src/main/java/org/springframework/graphql/observation/ExecutionRequestObservationContext.java index b07956dd..e7451695 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/observation/ExecutionRequestObservationContext.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/observation/ExecutionRequestObservationContext.java @@ -19,8 +19,7 @@ package org.springframework.graphql.observation; import graphql.ExecutionInput; import graphql.ExecutionResult; import io.micrometer.observation.Observation; - -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Context that holds information for metadata collection during observations @@ -33,8 +32,7 @@ public class ExecutionRequestObservationContext extends Observation.Context { private final ExecutionInput executionInput; - @Nullable - private ExecutionResult executionResult; + private @Nullable ExecutionResult executionResult; public ExecutionRequestObservationContext(ExecutionInput executionInput) { this.executionInput = executionInput; @@ -52,8 +50,7 @@ public class ExecutionRequestObservationContext extends Observation.Context { * Return the {@link ExecutionResult result} for the request execution. * @since 1.1.4 */ - @Nullable - public ExecutionResult getExecutionResult() { + public @Nullable ExecutionResult getExecutionResult() { return this.executionResult; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationInstrumentation.java b/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationInstrumentation.java index d5be3435..40218713 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationInstrumentation.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationInstrumentation.java @@ -42,10 +42,10 @@ import org.dataloader.DataLoader; import org.dataloader.DataLoaderRegistry; import org.dataloader.instrumentation.DataLoaderInstrumentation; import org.dataloader.instrumentation.DataLoaderInstrumentationContext; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.execution.SelfDescribingDataFetcher; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; /** * {@link graphql.execution.instrumentation.Instrumentation} that creates @@ -77,14 +77,11 @@ public class GraphQlObservationInstrumentation extends SimplePerformantInstrumen private final ObservationRegistry observationRegistry; - @Nullable - private final ExecutionRequestObservationConvention requestObservationConvention; + private final @Nullable ExecutionRequestObservationConvention requestObservationConvention; - @Nullable - private final DataFetcherObservationConvention dataFetcherObservationConvention; + private final @Nullable DataFetcherObservationConvention dataFetcherObservationConvention; - @Nullable - private final DataLoaderObservationConvention dataLoaderObservationConvention; + private final @Nullable DataLoaderObservationConvention dataLoaderObservationConvention; /** * Create an {@code GraphQlObservationInstrumentation} that records observations @@ -148,7 +145,7 @@ public class GraphQlObservationInstrumentation extends SimplePerformantInstrumen } @Override - public InstrumentationContext beginExecution(InstrumentationExecutionParameters parameters, + public @Nullable InstrumentationContext beginExecution(InstrumentationExecutionParameters parameters, InstrumentationState state) { if (state == RequestObservationInstrumentationState.INSTANCE) { ExecutionRequestObservationContext observationContext = new ExecutionRequestObservationContext(parameters.getExecutionInput()); @@ -235,8 +232,7 @@ public class GraphQlObservationInstrumentation extends SimplePerformantInstrumen return dataFetcher; } - @Nullable - private static Observation getCurrentObservation(DataFetchingEnvironment environment) { + private static @Nullable Observation getCurrentObservation(DataFetchingEnvironment environment) { Observation currentObservation = null; if (environment.getLocalContext() instanceof GraphQLContext localContext) { currentObservation = localContext.get(ObservationThreadLocalAccessor.KEY); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/observation/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/observation/package-info.java index 1901f993..fef3fcb6 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/observation/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/observation/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2025 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. @@ -17,9 +17,7 @@ /** * Support for GraphQL {@link io.micrometer.observation.Observation observability}. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.observation; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/package-info.java index 933810f6..cccef9a3 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -17,9 +17,7 @@ /** * Top level abstractions for processing GraphQL requests. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/DefaultWebGraphQlHandlerBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/server/DefaultWebGraphQlHandlerBuilder.java index d66cf536..a37e4acf 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/DefaultWebGraphQlHandlerBuilder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/DefaultWebGraphQlHandlerBuilder.java @@ -22,12 +22,12 @@ import java.util.List; import io.micrometer.context.ContextSnapshot; import io.micrometer.context.ContextSnapshotFactory; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.graphql.ExecutionGraphQlService; import org.springframework.graphql.execution.ContextPropagationHelper; import org.springframework.graphql.server.WebGraphQlInterceptor.Chain; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -42,11 +42,9 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder { private final List interceptors = new ArrayList<>(); - @Nullable - private ContextSnapshotFactory snapshotFactory; + private @Nullable ContextSnapshotFactory snapshotFactory; - @Nullable - private WebSocketGraphQlInterceptor webSocketInterceptor; + private @Nullable WebSocketGraphQlInterceptor webSocketInterceptor; DefaultWebGraphQlHandlerBuilder(ExecutionGraphQlService service) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/RSocketGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/server/RSocketGraphQlRequest.java index fddabeb8..b9bf82e6 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/RSocketGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/RSocketGraphQlRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +20,10 @@ import java.util.Locale; import java.util.Map; import io.rsocket.exceptions.RejectedException; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.ExecutionGraphQlRequest; import org.springframework.graphql.support.DefaultExecutionGraphQlRequest; -import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** @@ -43,16 +43,21 @@ public class RSocketGraphQlRequest extends DefaultExecutionGraphQlRequest implem * @param locale the locale from the HTTP request, if any */ public RSocketGraphQlRequest(Map body, String id, @Nullable Locale locale) { - super(getKey(QUERY_KEY, body), getKey(OPERATION_NAME_KEY, body), + super(getQuery(body), getKey(OPERATION_NAME_KEY, body), getKey(VARIABLES_KEY, body), getKey(EXTENSIONS_KEY, body), id, locale); } @SuppressWarnings("unchecked") - private static T getKey(String key, Map body) { - if (key.equals("query") && !StringUtils.hasText((String) body.get(key))) { - throw new RejectedException("No \"query\" in the request document"); - } + private static @Nullable T getKey(String key, Map body) { return (T) body.get(key); } + private static String getQuery(Map body) { + String query = getKey(QUERY_KEY, body); + if (!StringUtils.hasText(query)) { + throw new RejectedException("No \"query\" in the request document"); + } + return query; + } + } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java index 52c735e7..6bf59945 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java @@ -22,12 +22,13 @@ import java.util.Collections; import java.util.Locale; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.graphql.ExecutionGraphQlRequest; import org.springframework.graphql.GraphQlRequest; import org.springframework.graphql.support.DefaultExecutionGraphQlRequest; import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; @@ -59,8 +60,7 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements private final MultiValueMap cookies; - @Nullable - private final InetSocketAddress remoteAddress; + private final @Nullable InetSocketAddress remoteAddress; private final Map attributes; @@ -156,8 +156,7 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements return (String) value; } - @Nullable - private static String getOperation(Map body) { + private static @Nullable String getOperation(Map body) { Object value = body.get(OPERATION_NAME_KEY); if (value != null && !(value instanceof String)) { throw new ServerWebInputException("Invalid value for '" + OPERATION_NAME_KEY + "'"); @@ -166,8 +165,7 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements } @SuppressWarnings("unchecked") - @Nullable - private static Map getMap(String key, Map body) { + private static @Nullable Map getMap(String key, Map body) { Object value = body.get(key); if (value != null && !(value instanceof Map)) { throw new ServerWebInputException("Invalid value for '" + key + "'"); @@ -221,8 +219,7 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements * Return the remote address of the client, if available. * @since 1.3.0 */ - @Nullable - public InetSocketAddress getRemoteAddress() { + public @Nullable InetSocketAddress getRemoteAddress() { return this.remoteAddress; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketGraphQlRequest.java index fc50ee17..8c720812 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketGraphQlRequest.java @@ -22,9 +22,10 @@ import java.net.URI; import java.util.Locale; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketSessionInfo.java b/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketSessionInfo.java index 4290a5cc..b7956ad7 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketSessionInfo.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketSessionInfo.java @@ -21,10 +21,10 @@ import java.net.URI; import java.security.Principal; import java.util.Map; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.http.HttpHeaders; -import org.springframework.lang.Nullable; /** * Expose information about the underlying WebSocketSession including the @@ -64,7 +64,6 @@ public interface WebSocketSessionInfo { * For a server session this is the remote address where the handshake * request came from. For a client session, it is {@code null}. */ - @Nullable - InetSocketAddress getRemoteAddress(); + @Nullable InetSocketAddress getRemoteAddress(); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/server/package-info.java index 7a0d285a..0d8d86b8 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -17,9 +17,7 @@ /** * Server transports handling GraphQL requests over the HTTP, WebSocket, and RSocket. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.server; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/support/AbstractAuthenticationWebSocketInterceptor.java b/spring-graphql/src/main/java/org/springframework/graphql/server/support/AbstractAuthenticationWebSocketInterceptor.java index 7973cf22..17ba1397 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/support/AbstractAuthenticationWebSocketInterceptor.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/support/AbstractAuthenticationWebSocketInterceptor.java @@ -82,8 +82,11 @@ public abstract class AbstractAuthenticationWebSocketInterceptor implements WebS } Map attributes = webSocketRequest.getSessionInfo().getAttributes(); SecurityContext securityContext = (SecurityContext) attributes.get(this.authenticationAttribute); - ContextView contextView = getContextToWrite(securityContext); - return chain.next(request).contextWrite(contextView); + if (securityContext != null) { + ContextView contextView = getContextToWrite(securityContext); + return chain.next(request).contextWrite(contextView); + } + return chain.next(request); } /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/support/BearerTokenAuthenticationExtractor.java b/spring-graphql/src/main/java/org/springframework/graphql/server/support/BearerTokenAuthenticationExtractor.java index bbc3acd9..6774233d 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/support/BearerTokenAuthenticationExtractor.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/support/BearerTokenAuthenticationExtractor.java @@ -20,9 +20,9 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; -import org.springframework.lang.Nullable; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.server.resource.BearerTokenError; @@ -89,8 +89,7 @@ public final class BearerTokenAuthenticationExtractor implements AuthenticationE return Mono.just(new BearerTokenAuthenticationToken(token)); } - @Nullable - private String getAuthorizationValue(Map payload) { + private @Nullable String getAuthorizationValue(Map payload) { String value = (String) payload.get(this.authorizationKey); if (value != null) { return value; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/support/GraphQlWebSocketMessage.java b/spring-graphql/src/main/java/org/springframework/graphql/server/support/GraphQlWebSocketMessage.java index d510eb8e..ff360d78 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/support/GraphQlWebSocketMessage.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/support/GraphQlWebSocketMessage.java @@ -22,9 +22,9 @@ import java.util.Map; import java.util.stream.Collectors; import graphql.GraphQLError; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.GraphQlRequest; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -38,14 +38,11 @@ import org.springframework.util.ObjectUtils; */ public class GraphQlWebSocketMessage { - @Nullable - private String id; + private @Nullable String id; - @Nullable - private GraphQlWebSocketMessageType type; + private @Nullable GraphQlWebSocketMessageType type; - @Nullable - private Object payload; + private @Nullable Object payload; /** @@ -73,8 +70,7 @@ public class GraphQlWebSocketMessage { * Return the request id that is applicable to messages associated with a * request, or {@code null} for connection level messages. */ - @Nullable - public String getId() { + public @Nullable String getId() { return this.id; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/support/SerializableGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/server/support/SerializableGraphQlRequest.java index 1e9878ba..d1cd662d 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/support/SerializableGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/support/SerializableGraphQlRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,13 @@ package org.springframework.graphql.server.support; +import java.util.Collections; import java.util.Map; import graphql.execution.preparsed.persisted.PersistedQuerySupport; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.GraphQlRequest; -import org.springframework.lang.Nullable; import org.springframework.web.server.ServerWebInputException; @@ -33,25 +34,20 @@ import org.springframework.web.server.ServerWebInputException; */ public class SerializableGraphQlRequest implements GraphQlRequest { - @Nullable - private String query; + private @Nullable String query; - @Nullable - private String operationName; + private @Nullable String operationName; - @Nullable - private Map variables; + private Map variables = Collections.emptyMap(); - @Nullable - private Map extensions; + private Map extensions = Collections.emptyMap(); public void setQuery(String query) { this.query = query; } - @Nullable - public String getQuery() { + public @Nullable String getQuery() { return this.query; } @@ -59,9 +55,8 @@ public class SerializableGraphQlRequest implements GraphQlRequest { this.operationName = operationName; } - @Nullable @Override - public String getOperationName() { + public @Nullable String getOperationName() { return this.operationName; } @@ -69,7 +64,6 @@ public class SerializableGraphQlRequest implements GraphQlRequest { this.variables = variables; } - @Nullable @Override public Map getVariables() { return this.variables; @@ -79,7 +73,6 @@ public class SerializableGraphQlRequest implements GraphQlRequest { this.extensions = extensions; } - @Nullable @Override public Map getExtensions() { return this.extensions; @@ -88,7 +81,7 @@ public class SerializableGraphQlRequest implements GraphQlRequest { @Override public String getDocument() { if (this.query == null) { - if (this.extensions != null && this.extensions.get("persistedQuery") != null) { + if (this.extensions.get("persistedQuery") != null) { return PersistedQuerySupport.PERSISTED_QUERY_MARKER; } throw new ServerWebInputException("No 'query'"); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/support/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/server/support/package-info.java index 3537c5a9..797b8dda 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/support/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/support/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -17,9 +17,7 @@ /** * Support classes for Web transports. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.server.support; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/AbstractGraphQlHttpHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/AbstractGraphQlHttpHandler.java index 0f038c43..4d52c53f 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/AbstractGraphQlHttpHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/AbstractGraphQlHttpHandler.java @@ -22,6 +22,7 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.core.io.buffer.DataBuffer; @@ -34,7 +35,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.InvalidMediaTypeException; import org.springframework.http.MediaType; import org.springframework.http.codec.CodecConfigurer; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -57,8 +57,7 @@ public abstract class AbstractGraphQlHttpHandler { private final WebGraphQlHandler graphQlHandler; - @Nullable - private final HttpCodecDelegate codecDelegate; + private final @Nullable HttpCodecDelegate codecDelegate; protected AbstractGraphQlHttpHandler( diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java index af64e382..2ed9114a 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlRequestPredicates.java @@ -21,6 +21,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.MediaTypes; import org.springframework.http.HttpHeaders; @@ -28,7 +29,6 @@ import org.springframework.http.HttpMethod; import org.springframework.http.InvalidMediaTypeException; import org.springframework.http.MediaType; import org.springframework.http.server.PathContainer; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.MimeTypeUtils; import org.springframework.web.cors.reactive.CorsUtils; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlSseHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlSseHandler.java index cb3bd1e3..785a23c2 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlSseHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlSseHandler.java @@ -25,6 +25,7 @@ import graphql.ErrorType; import graphql.ExecutionResult; import graphql.GraphQLError; import graphql.GraphqlErrorBuilder; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -34,7 +35,6 @@ import org.springframework.graphql.server.WebGraphQlHandler; import org.springframework.graphql.server.WebGraphQlResponse; import org.springframework.http.MediaType; import org.springframework.http.codec.ServerSentEvent; -import org.springframework.lang.Nullable; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; @@ -57,11 +57,9 @@ public class GraphQlSseHandler extends AbstractGraphQlHttpHandler { private static final Mono>> COMPLETE_EVENT_MONO = Mono.just(ServerSentEvent.>builder(Collections.emptyMap()).event("complete").build()); - @Nullable - private final Duration timeout; + private final @Nullable Duration timeout; - @Nullable - private final Duration keepAliveDuration; + private final @Nullable Duration keepAliveDuration; /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlWebSocketHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlWebSocketHandler.java index 264ca9f1..09c79cc8 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlWebSocketHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlWebSocketHandler.java @@ -32,6 +32,7 @@ import graphql.GraphQLError; import graphql.GraphqlErrorBuilder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; import reactor.core.publisher.Flux; @@ -48,7 +49,6 @@ import org.springframework.graphql.server.WebSocketSessionInfo; import org.springframework.graphql.server.support.GraphQlWebSocketMessage; import org.springframework.http.HttpHeaders; import org.springframework.http.codec.CodecConfigurer; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.web.reactive.socket.CloseStatus; @@ -81,8 +81,7 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { private final Duration initTimeoutDuration; - @Nullable - private final Duration keepAliveDuration; + private final @Nullable Duration keepAliveDuration; /** @@ -141,7 +140,7 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { // Session state WebSocketSessionInfo sessionInfo = new WebFluxSessionInfo(session); - AtomicReference> connectionInitPayloadRef = new AtomicReference<>(); + AtomicReference<@Nullable Map> connectionInitPayloadRef = new AtomicReference<>(); Map subscriptions = new ConcurrentHashMap<>(); Mono.delay(this.initTimeoutDuration) @@ -164,6 +163,9 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { return session.send(session.receive().flatMap((webSocketMessage) -> { GraphQlWebSocketMessage message = this.codecDelegate.decode(webSocketMessage); + if (message == null) { + return GraphQlStatus.close(session, GraphQlStatus.INVALID_MESSAGE_STATUS); + } String id = message.getId(); Map payload = message.getPayload(); switch (message.resolvedType()) { @@ -329,7 +331,7 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { } @Override - public InetSocketAddress getRemoteAddress() { + public @Nullable InetSocketAddress getRemoteAddress() { return this.session.getHandshakeInfo().getRemoteAddress(); } } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/WebSocketCodecDelegate.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/WebSocketCodecDelegate.java index 4c73ebdc..62affeb4 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/WebSocketCodecDelegate.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/WebSocketCodecDelegate.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import graphql.GraphQLError; +import org.jspecify.annotations.Nullable; import org.springframework.core.ResolvableType; import org.springframework.core.codec.Decoder; @@ -88,7 +89,7 @@ final class WebSocketCodecDelegate { } @SuppressWarnings("ConstantConditions") - GraphQlWebSocketMessage decode(WebSocketMessage webSocketMessage) { + @Nullable GraphQlWebSocketMessage decode(WebSocketMessage webSocketMessage) { DataBuffer buffer = DataBufferUtils.retain(webSocketMessage.getPayload()); return (GraphQlWebSocketMessage) this.decoder.decode(buffer, MESSAGE_TYPE, null, null); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/package-info.java index 949f295e..0fcfa6fb 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -17,9 +17,7 @@ /** * HTTP and WebSocket handlers for use in a Spring WebFlux application. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.server.webflux; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/AbstractGraphQlHttpHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/AbstractGraphQlHttpHandler.java index a1604382..57442cb8 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/AbstractGraphQlHttpHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/AbstractGraphQlHttpHandler.java @@ -26,6 +26,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.context.i18n.LocaleContextHolder; @@ -43,7 +44,6 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; -import org.springframework.lang.Nullable; import org.springframework.util.AlternativeJdkIdGenerator; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -75,8 +75,7 @@ public abstract class AbstractGraphQlHttpHandler { private final WebGraphQlHandler graphQlHandler; - @Nullable - private final HttpMessageConverter messageConverter; + private final @Nullable HttpMessageConverter messageConverter; @SuppressWarnings("unchecked") @@ -97,8 +96,7 @@ public abstract class AbstractGraphQlHttpHandler { * @return the write function, or {@code null} if a * {@code HttpMessageConverter} was not provided to the constructor */ - @Nullable - protected ServerResponse.HeadersBuilder.WriteFunction getWriteFunction( + protected ServerResponse.HeadersBuilder.@Nullable WriteFunction getWriteFunction( Map resultMap, MediaType contentType) { return (this.messageConverter != null) ? @@ -221,7 +219,7 @@ public abstract class AbstractGraphQlHttpHandler { implements ServerResponse.HeadersBuilder.WriteFunction { @Override - public ModelAndView write(HttpServletRequest request, HttpServletResponse response) throws Exception { + public @Nullable ModelAndView write(HttpServletRequest request, HttpServletResponse response) throws Exception { ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); this.converter.write(this.resultMap, this.contentType, httpResponse); return null; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java index 3490dac8..4aefe097 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java @@ -19,6 +19,7 @@ package org.springframework.graphql.server.webmvc; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.graphql.MediaTypes; @@ -29,7 +30,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.InvalidMediaTypeException; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.lang.Nullable; import org.springframework.web.server.NotAcceptableStatusException; import org.springframework.web.servlet.function.ServerRequest; import org.springframework.web.servlet.function.ServerResponse; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java index b3091138..3e34ea31 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlRequestPredicates.java @@ -21,6 +21,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.MediaTypes; import org.springframework.http.HttpHeaders; @@ -28,7 +29,6 @@ import org.springframework.http.HttpMethod; import org.springframework.http.InvalidMediaTypeException; import org.springframework.http.MediaType; import org.springframework.http.server.PathContainer; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.MimeTypeUtils; import org.springframework.web.cors.CorsUtils; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlSseHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlSseHandler.java index 8171b983..16b07791 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlSseHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlSseHandler.java @@ -27,6 +27,7 @@ import graphql.ExecutionResult; import graphql.GraphQLError; import graphql.GraphqlErrorBuilder; import org.apache.commons.logging.Log; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.BaseSubscriber; import reactor.core.publisher.Flux; @@ -36,7 +37,6 @@ import reactor.core.publisher.Sinks; import org.springframework.graphql.execution.SubscriptionPublisherException; import org.springframework.graphql.server.WebGraphQlHandler; import org.springframework.graphql.server.WebGraphQlResponse; -import org.springframework.lang.Nullable; import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.servlet.function.ServerRequest; import org.springframework.web.servlet.function.ServerResponse; @@ -56,11 +56,9 @@ public class GraphQlSseHandler extends AbstractGraphQlHttpHandler { private static final Map HEARTBEAT_MAP = new LinkedHashMap<>(0); - @Nullable - private final Duration timeout; + private final @Nullable Duration timeout; - @Nullable - private final Duration keepAliveDuration; + private final @Nullable Duration keepAliveDuration; /** diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java index 43ef24c4..4492e82e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java @@ -39,6 +39,7 @@ import io.micrometer.context.ContextSnapshot; import io.micrometer.context.ContextSnapshotFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; import reactor.core.publisher.BaseSubscriber; @@ -63,7 +64,6 @@ import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.web.socket.CloseStatus; @@ -104,8 +104,7 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub private final HttpMessageConverter converter; - @Nullable - private final Duration keepAliveDuration; + private final @Nullable Duration keepAliveDuration; private final Map sessionInfoMap = new ConcurrentHashMap<>(); @@ -495,8 +494,7 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub return this.sessionInfo; } - @Nullable - Map getConnectionInitPayload() { + @Nullable Map getConnectionInitPayload() { return this.connectionInitPayloadRef.get(); } @@ -553,8 +551,9 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub @Override public URI getUri() { - Assert.notNull(this.session.getUri(), "Expected URI"); - return this.session.getUri(); + URI uri = this.session.getUri(); + Assert.notNull(uri, "Expected URI"); + return uri; } @Override @@ -568,7 +567,7 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub } @Override - public InetSocketAddress getRemoteAddress() { + public @Nullable InetSocketAddress getRemoteAddress() { return this.session.getRemoteAddress(); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/package-info.java index 46299ab6..d2ac304c 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2025 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. @@ -17,9 +17,7 @@ /** * HTTP and WebSocket handlers for use in a Spring WebMvc application. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.server.webmvc; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/support/AbstractGraphQlResponse.java b/spring-graphql/src/main/java/org/springframework/graphql/support/AbstractGraphQlResponse.java index e3222678..b5f3d18b 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/support/AbstractGraphQlResponse.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/support/AbstractGraphQlResponse.java @@ -23,10 +23,11 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; + import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.ResponseError; import org.springframework.graphql.ResponseField; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -58,8 +59,7 @@ public abstract class AbstractGraphQlResponse implements GraphQlResponse { private final List parsedPath; - @Nullable - private final Object value; + private final @Nullable Object value; private final List fieldErrors; @@ -112,8 +112,7 @@ public abstract class AbstractGraphQlResponse implements GraphQlResponse { return dataPath; } - @Nullable - private static Object initFieldValue(List path, GraphQlResponse response) { + private static @Nullable Object initFieldValue(List path, GraphQlResponse response) { Object value = (response.isValid() ? response.getData() : null); for (Object segment : path) { if (value == null) { @@ -162,7 +161,7 @@ public abstract class AbstractGraphQlResponse implements GraphQlResponse { @SuppressWarnings("unchecked") @Override - public T getValue() { + public @Nullable T getValue() { return (T) this.value; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlRequest.java index d05171fc..425645d8 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlRequest.java @@ -24,10 +24,10 @@ import java.util.function.BiFunction; import graphql.ExecutionInput; import graphql.execution.ExecutionId; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.ExecutionGraphQlRequest; import org.springframework.graphql.GraphQlRequest; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -47,8 +47,7 @@ public class DefaultExecutionGraphQlRequest extends DefaultGraphQlRequest implem private final String id; - @Nullable - private ExecutionId executionId; + private @Nullable ExecutionId executionId; private final Locale locale; @@ -88,8 +87,7 @@ public class DefaultExecutionGraphQlRequest extends DefaultGraphQlRequest implem } @Override - @Nullable - public ExecutionId getExecutionId() { + public @Nullable ExecutionId getExecutionId() { return this.executionId; } @@ -125,7 +123,7 @@ public class DefaultExecutionGraphQlRequest extends DefaultGraphQlRequest implem @Override public String toString() { - return super.toString() + ", id=" + getId() + ((getLocale() != null) ? ", Locale=" + getLocale() : ""); + return super.toString() + ", id=" + getId() + ", Locale=" + getLocale(); } } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlResponse.java b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlResponse.java index 37bd3d0b..0f28eb81 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlResponse.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultExecutionGraphQlResponse.java @@ -27,11 +27,11 @@ import graphql.ExecutionResult; import graphql.ExecutionResultImpl; import graphql.GraphQLError; import graphql.language.SourceLocation; +import org.jspecify.annotations.Nullable; import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.GraphQlResponse; import org.springframework.graphql.ResponseError; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -85,9 +85,8 @@ public class DefaultExecutionGraphQlResponse extends AbstractGraphQlResponse imp return (this.result.isDataPresent() && this.result.getData() != null); } - @Nullable @Override - public T getData() { + public @Nullable T getData() { return this.result.getData(); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultGraphQlRequest.java index e118463a..a18245bd 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/support/DefaultGraphQlRequest.java @@ -20,8 +20,9 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.graphql.GraphQlRequest; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; @@ -46,8 +47,7 @@ public class DefaultGraphQlRequest implements GraphQlRequest { private final String document; - @Nullable - private final String operationName; + private final @Nullable String operationName; private final Map variables; @@ -87,8 +87,7 @@ public class DefaultGraphQlRequest implements GraphQlRequest { } @Override - @Nullable - public String getOperationName() { + public @Nullable String getOperationName() { return this.operationName; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/support/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/support/package-info.java index 02c31774..6680d9e6 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/support/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/support/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2025 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. @@ -17,9 +17,7 @@ /** * Support classes for Spring GraphQL. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.graphql.support; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked;