Skip to content

feat(gax-java): add JSpecify Null annotations to gax#13799

Draft
nnicolee wants to merge 33 commits into
mainfrom
feat/jspecify-gax-auth
Draft

feat(gax-java): add JSpecify Null annotations to gax#13799
nnicolee wants to merge 33 commits into
mainfrom
feat/jspecify-gax-auth

Conversation

@nnicolee

@nnicolee nnicolee commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR migrates the GAX (sdk-platform-java/gax-java) library to use JSpecify 1.0.0 nullability annotations (@NullMarked and @Nullable), replacing the previous javax.annotation references.

Key Changes

JSpecify 1.0.0 Annotation Adoption in GAX:

  • Applied @NullMarked at the class and package levels across GAX core classes to define non-null by default semantics.
  • Replaced imports of javax.annotation.Nullable with org.jspecify.annotations.Nullable.
  • Adopted type-use nullability declarations for generic parameters, arrays, and API settings (e.g. java.time.@Nullable Duration, List<@Nullable T>).

Verification Results

  • testing doc tba

gemini-code-assist[bot]

This comment was marked as outdated.

@nnicolee
nnicolee requested a review from lqiu96 July 17, 2026 05:12
nnicolee and others added 25 commits July 17, 2026 17:27
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
@lqiu96

lqiu96 commented Jul 21, 2026

Copy link
Copy Markdown
Member

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 131 to 133
@Nullable
@GuardedBy("lock")
private volatile ScheduledFuture<?> timeoutFuture;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
@Nullable
@GuardedBy("lock")
private volatile ScheduledFuture<?> timeoutFuture;
@GuardedBy("lock")
private volatile @Nullable ScheduledFuture<?> timeoutFuture;

Comment on lines 58 to +62
public ApiException(
Throwable cause, StatusCode statusCode, boolean retryable, ErrorDetails errorDetails) {
Throwable cause,
StatusCode statusCode,
boolean retryable,
@Nullable ErrorDetails errorDetails) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since cause can be null, it should be annotated with @Nullable to prevent static analysis errors under @NullMarked.

Suggested change
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) {

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed for 'gapic-generator-java-root'

Failed conditions
B Maintainability Rating on New Code (required ≥ A)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

💡 Need a hand with PR review? Try Gitar by Sonar!

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed for 'gapic-generator-java-root'

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)
B Maintainability Rating on New Code (required ≥ A)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

💡 Need a hand with PR review? Try Gitar by Sonar!

@lqiu96

lqiu96 commented Jul 21, 2026

Copy link
Copy Markdown
Member

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
protected Builder(StubSettings.@Nullable Builder stubSettings) {
protected Builder(StubSettings.Builder stubSettings) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants