Skip to content

feat(auth): add JSpecify Null annotations to Auth#13842

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

feat(auth): add JSpecify Null annotations to Auth#13842
nnicolee wants to merge 6 commits into
mainfrom
feat/jspecify-auth

Conversation

@nnicolee

Copy link
Copy Markdown
Contributor

tba

@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 several modules, including GAX and Google Cloud Bigtable, and updates tests to configure Mockito mocks to ignore annotations. The reviewer feedback highlights several improvement opportunities, such as correcting a potentially unsafe @nullable annotation on a builder constructor parameter to avoid NullPointerExceptions, and ensuring that the previousThrowable parameters in retry algorithms are explicitly marked as @nullable to handle successful attempts where no exception is thrown.


/** 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

Annotating stubSettings as @Nullable suggests that null is a valid argument. However, if stubSettings is null, calling methods like getQuotaProjectId() or getWatchdogProvider() on this builder will result in a NullPointerException because they dereference stubSettings without a null check. If stubSettings must not be null, it should not be annotated with @Nullable, and ideally, a null check like Preconditions.checkNotNull(stubSettings) should be added.

* thrown instead
* @param previousSettings previous attempt settings
* @return next attempt settings, can be {@code null}, if there should be no new attempt
*/

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 a @NullMarked class, previousThrowable should be annotated with @Nullable because on successful attempts (e.g., during polling or when checking if retries should continue after a success), previousThrowable can be null.

Suggested change
*/
@Nullable Throwable previousThrowable,

public boolean shouldRetry(
RetryingContext context,
@Nullable RetryingContext context,
Throwable previousThrowable,

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 a @NullMarked class, previousThrowable should be annotated with @Nullable because on successful attempts (e.g., during polling or when checking if retries should continue after a success), previousThrowable can be null.

Suggested change
Throwable previousThrowable,
@Nullable Throwable previousThrowable,

public TimedAttemptSettings createNextAttempt(
RetryingContext context,
@Nullable RetryingContext context,
Throwable previousThrowable,

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 a @NullMarked class, previousThrowable should be annotated with @Nullable because on successful attempts (e.g., during polling or when checking if retries should continue after a success), previousThrowable can be null.

Suggested change
Throwable previousThrowable,
@Nullable Throwable previousThrowable,

public boolean shouldRetry(
RetryingContext context,
@Nullable RetryingContext context,
Throwable previousThrowable,

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 a @NullMarked class, previousThrowable should be annotated with @Nullable because on successful attempts (e.g., during polling or when checking if retries should continue after a success), previousThrowable can be null.

Suggested change
Throwable previousThrowable,
@Nullable Throwable previousThrowable,

@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!

@nnicolee
nnicolee force-pushed the feat/jspecify-auth branch from 76c939a to afdc8c6 Compare July 21, 2026 04:39
@nnicolee
nnicolee force-pushed the feat/jspecify-auth branch from afdc8c6 to 432c5d0 Compare July 21, 2026 04:43
@nnicolee
nnicolee requested a review from lqiu96 July 21, 2026 15:12
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<version>1.0.0</version>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: Can you declare this as a property, similar to above?

e.g. ${project.jspecify.version}

LoggerFactoryProvider mockLoggerFactoryProvider = mock(LoggerFactoryProvider.class);
ILoggerFactory mockLoggerFactory = mock(ILoggerFactory.class);
LoggerFactoryProvider mockLoggerFactoryProvider =
mock(LoggerFactoryProvider.class, org.mockito.Mockito.withSettings().withoutAnnotations());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: Just a few occurrences of the fully qualified names. See if you can import Mockito

@lqiu96

lqiu96 commented Jul 22, 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 nullness annotations (@NullMarked and @nullable) across several modules, including appengine, cab-token-generator, credentials, and oauth2_http, along with updating the build configurations to include the jspecify dependency. The feedback highlights several nullability mismatches introduced by these changes: changing getRequestMetadata in the abstract Credentials class to accept @nullable URI requires updating all subclass implementations to avoid Liskov Substitution Principle violations; returning @nullable PublicKey from buildPublicKey could result in inserting nulls into a map expecting non-null values; and passing a @nullable URI from createSelfSignedJwtCredentials to getUriForSelfSignedJWT (which expects a non-null parameter) will trigger static analysis warnings.

Comment on lines +167 to +168
public abstract Map<String, List<String>> getRequestMetadata(@Nullable URI uri)
throws IOException;

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.

high

Since Credentials is the base class and getRequestMetadata is an abstract method, changing its parameter uri to @nullable requires all concrete subclasses overriding this method to also be updated to accept @nullable URI. Since classes like OAuth2Credentials, ApiKeyCredentials, JwtCredentials, and ServiceAccountJwtAccessCredentials are annotated with @NullMarked in this PR, their implementations of getRequestMetadata will default to expecting a non-null URI parameter. This mismatch will cause static analysis or compilation errors due to Liskov Substitution Principle violations. Please ensure that all subclass implementations of getRequestMetadata are also updated to use @nullable URI.

}

private PublicKey buildPublicKey(JsonWebKey key)
private @Nullable PublicKey buildPublicKey(JsonWebKey key)

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 method buildPublicKey is updated to return @nullable PublicKey. However, the caller load returns a Map<String, PublicKey> (which under @NullMarked implies non-null keys and values). If buildPublicKey returns null and this value is put into keyCache, it will violate the non-null value constraint of the map, leading to static analysis errors or potential NullPointerExceptions down the line. Please ensure that null public keys are filtered out and not put into keyCache, or update the map signature to Map<String, @nullable PublicKey> if null values are expected.

Comment on lines +1014 to 1016
JwtCredentials createSelfSignedJwtCredentials(final @Nullable URI uri) {
return createSelfSignedJwtCredentials(uri, scopes.isEmpty() ? defaultScopes : scopes);
}

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 parameter uri in createSelfSignedJwtCredentials is now annotated with @nullable. However, the helper method getUriForSelfSignedJWT(URI uri) (defined on line 1011) is not updated to accept @nullable URI. Under @NullMarked, passing a @nullable URI to getUriForSelfSignedJWT will trigger static analysis warnings/errors. Please update getUriForSelfSignedJWT to also accept @nullable URI or handle the null case before calling it.

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