feat(gax-java): add JSpecify Null annotations to gax#13799
Conversation
…-cloud-java into feat/jspecify-gax-auth
…-cloud-java into feat/jspecify-gax-auth
Disable annotations in Mockito mocks for GAX classes across 17 test files in `google-cloud-bigtable` to avoid ArrayStoreException on Java 8. These tests were missed in the previous round of fixes. TAG=agy CONV=de3ef09d-a26c-4190-b578-4c993f807bdc
…-cloud-java into feat/jspecify-gax-auth
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces JSpecify nullness annotations (@NullMarked and @Nullable) across the gax-grpc, gax-httpjson, and gax modules, updating build configurations and adjusting mock creation in tests to avoid annotation-related issues. The review feedback focuses on resolving static analysis warnings under the new @NullMarked context by adding missing @Nullable annotations to parameters and variables that can be null in practice (such as previous attempt results, exceptions, and causes). Additionally, the reviewer recommended standardizing the ordering and placement of annotations, such as placing declaration annotations like @GuardedBy before type-use annotations, and ensuring @Nullable is positioned correctly relative to modifiers like volatile.
| @Nullable | ||
| @GuardedBy("lock") | ||
| private volatile ScheduledFuture<?> timeoutFuture; |
There was a problem hiding this comment.
In Java, declaration annotations like @GuardedBy should be placed before type-use annotations like @Nullable, and type-use annotations should be placed immediately before the type they apply to (after modifiers like volatile). Placing @GuardedBy first and @Nullable after volatile is the standard order and improves readability.
| @Nullable | |
| @GuardedBy("lock") | |
| private volatile ScheduledFuture<?> timeoutFuture; | |
| @GuardedBy("lock") | |
| private volatile @Nullable ScheduledFuture<?> timeoutFuture; |
| public ApiException( | ||
| Throwable cause, StatusCode statusCode, boolean retryable, ErrorDetails errorDetails) { | ||
| Throwable cause, | ||
| StatusCode statusCode, | ||
| boolean retryable, | ||
| @Nullable ErrorDetails errorDetails) { |
There was a problem hiding this comment.
Since cause can be null, it should be annotated with @Nullable to prevent static analysis errors under @NullMarked.
| public ApiException( | |
| Throwable cause, StatusCode statusCode, boolean retryable, ErrorDetails errorDetails) { | |
| Throwable cause, | |
| StatusCode statusCode, | |
| boolean retryable, | |
| @Nullable ErrorDetails errorDetails) { | |
| public ApiException( | |
| @Nullable Throwable cause, | |
| StatusCode statusCode, | |
| boolean retryable, | |
| @Nullable ErrorDetails errorDetails) { |
…-cloud-java into feat/jspecify-gax-auth
|
|
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request integrates JSpecify's null-safety annotations (@NullMarked and @Nullable) across the gax, gax-grpc, and gax-httpjson modules, and updates Mockito tests to handle these annotations correctly. The review feedback highlights opportunities to improve null-safety completeness by explicitly annotating potentially null fields (such as channel and endpointContext in GrpcCallContext and HttpJsonCallContext) with @Nullable, and recommends removing an incorrect @Nullable annotation on the stubSettings parameter in ClientSettings to prevent potential dereference issues.
| @@ -85,14 +87,14 @@ public final class GrpcCallContext implements ApiCallContext { | |||
| public static final CallOptions.Key<ApiTracer> TRACER_KEY = CallOptions.Key.create("gax.tracer"); | |||
|
|
|||
| private final Channel channel; | |||
There was a problem hiding this comment.
Under @NullMarked, fields that can be null must be explicitly annotated with @Nullable. Since the constructor parameter channel is annotated with @Nullable and GrpcCallContext can be instantiated with a null channel (e.g., via createDefault()), the channel field should also be annotated with @Nullable to avoid static analysis warnings or potential null safety issues.
| private final Channel channel; | |
| private final @Nullable Channel channel; |
| private final @Nullable ImmutableSet<StatusCode.Code> retryableCodes; | ||
| private final ImmutableMap<String, List<String>> extraHeaders; | ||
| private final ApiCallContextOptions options; | ||
| private final EndpointContext endpointContext; |
There was a problem hiding this comment.
Under @NullMarked, fields that can be null must be explicitly annotated with @Nullable. Since the constructor parameter endpointContext is annotated with @Nullable and GrpcCallContext can be instantiated with a null endpoint context, the endpointContext field should also be annotated with @Nullable to avoid static analysis warnings or potential null safety issues.
| private final EndpointContext endpointContext; | |
| private final @Nullable EndpointContext endpointContext; |
| public final class HttpJsonCallContext implements ApiCallContext { | ||
| private static final HttpJsonStatusCode UNAUTHENTICATED_STATUS_CODE = | ||
| HttpJsonStatusCode.of(StatusCode.Code.UNAUTHENTICATED); | ||
| private final HttpJsonChannel channel; |
There was a problem hiding this comment.
Under @NullMarked, fields that can be null must be explicitly annotated with @Nullable. Since the constructor parameter channel is annotated with @Nullable and HttpJsonCallContext can be instantiated with a null channel (e.g., via createDefault()), the channel field should also be annotated with @Nullable to avoid static analysis warnings or potential null safety issues.
| private final HttpJsonChannel channel; | |
| private final @Nullable HttpJsonChannel channel; |
| @Nullable private final ImmutableSet<StatusCode.Code> retryableCodes; | ||
| private final @Nullable RetrySettings retrySettings; | ||
| private final @Nullable ImmutableSet<StatusCode.Code> retryableCodes; | ||
| private final EndpointContext endpointContext; |
There was a problem hiding this comment.
Under @NullMarked, fields that can be null must be explicitly annotated with @Nullable. Since the constructor parameter endpointContext is annotated with @Nullable and HttpJsonCallContext can be instantiated with a null endpoint context, the endpointContext field should also be annotated with @Nullable to avoid static analysis warnings or potential null safety issues.
| private final EndpointContext endpointContext; | |
| private final @Nullable EndpointContext endpointContext; |
|
|
||
| /** Create a builder from a StubSettings object. */ | ||
| protected Builder(StubSettings.Builder stubSettings) { | ||
| protected Builder(StubSettings.@Nullable Builder stubSettings) { |
There was a problem hiding this comment.
The constructor parameter stubSettings is annotated with StubSettings.@Nullable Builder, but the field this.stubSettings is non-null and dereferenced directly throughout the class (e.g., in getQuotaProjectId()) without null checks. To maintain null safety and avoid static analysis warnings, the parameter should not be annotated with @Nullable if a non-null builder is required.
| protected Builder(StubSettings.@Nullable Builder stubSettings) { | |
| protected Builder(StubSettings.Builder stubSettings) { |


Summary
This PR migrates the GAX (
sdk-platform-java/gax-java) library to use JSpecify 1.0.0 nullability annotations (@NullMarkedand@Nullable), replacing the previousjavax.annotationreferences.Key Changes
JSpecify 1.0.0 Annotation Adoption in GAX:
@NullMarkedat the class and package levels across GAX core classes to define non-null by default semantics.javax.annotation.Nullablewithorg.jspecify.annotations.Nullable.java.time.@Nullable Duration,List<@Nullable T>).Verification Results