This is an automated email from the ASF dual-hosted git repository.

kennknowles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 3afe19a1a4c Merge pull request #40146: Remove blanket nullness 
suppressions in google-cloud-platform-core
3afe19a1a4c is described below

commit 3afe19a1a4c4c438038b3ec7972f3194df157f54
Author: Nitin Ware <[email protected]>
AuthorDate: Thu Sep 24 09:20:50 2026 -0500

    Merge pull request #40146: Remove blanket nullness suppressions in 
google-cloud-platform-core
---
 .../extensions/gcp/auth/NoopCredentialFactory.java | 13 ++--
 .../sdk/extensions/gcp/options/GcpOptions.java     | 19 +++--
 .../gcp/options/GoogleApiDebugOptions.java         | 13 ++--
 .../sdk/extensions/gcp/storage/GcsFileSystem.java  | 12 ++--
 .../sdk/extensions/gcp/storage/GcsResourceId.java  |  8 +--
 .../sdk/extensions/gcp/util/CustomHttpErrors.java  |  6 +-
 .../beam/sdk/extensions/gcp/util/GcpSecret.java    |  2 +
 .../beam/sdk/extensions/gcp/util/GcsUtilV1.java    | 81 ++++++++++++++++------
 .../beam/sdk/extensions/gcp/util/GcsUtilV2.java    |  8 +--
 .../gcp/util/RetryHttpRequestInitializer.java      | 17 +++--
 .../gcp/util/UploadIdResponseInterceptor.java      |  3 -
 .../sdk/extensions/gcp/util/gcsfs/GcsPath.java     | 44 +++++++-----
 12 files changed, 137 insertions(+), 89 deletions(-)

diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/auth/NoopCredentialFactory.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/auth/NoopCredentialFactory.java
index 5035c6e9bcc..48817cf21e4 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/auth/NoopCredentialFactory.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/auth/NoopCredentialFactory.java
@@ -23,14 +23,12 @@ import java.net.URI;
 import java.util.List;
 import java.util.Map;
 import org.apache.beam.sdk.options.PipelineOptions;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * Construct an oauth credential to be used by the SDK and the SDK workers. 
Always returns a null
  * Credential object.
  */
-@SuppressWarnings({
-  "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
 public class NoopCredentialFactory implements CredentialFactory {
   private static final NoopCredentialFactory INSTANCE = new 
NoopCredentialFactory();
   private static final NoopCredentials NOOP_CREDENTIALS = new 
NoopCredentials();
@@ -45,13 +43,18 @@ public class NoopCredentialFactory implements 
CredentialFactory {
   }
 
   private static class NoopCredentials extends Credentials {
+    // google-auth Credentials is not annotated for nullness; these overrides 
intentionally
+    // return null (this is a no-op credential), which its API permits.
     @Override
-    public String getAuthenticationType() {
+    @SuppressWarnings("nullness")
+    public @Nullable String getAuthenticationType() {
       return null;
     }
 
     @Override
-    public Map<String, List<String>> getRequestMetadata(URI uri) throws 
IOException {
+    @SuppressWarnings("nullness")
+    public @Nullable Map<String, List<String>> getRequestMetadata(@Nullable 
URI uri)
+        throws IOException {
       return null;
     }
 
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/options/GcpOptions.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/options/GcpOptions.java
index 0b3be309615..1d65d1c2426 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/options/GcpOptions.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/options/GcpOptions.java
@@ -17,6 +17,7 @@
  */
 package org.apache.beam.sdk.extensions.gcp.options;
 
+import static org.apache.beam.sdk.util.Preconditions.checkArgumentNotNull;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Strings.isNullOrEmpty;
 
@@ -42,7 +43,6 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import org.apache.beam.sdk.extensions.gcp.auth.CredentialFactory;
@@ -80,9 +80,6 @@ import org.slf4j.LoggerFactory;
  * mechanisms for creating credentials.
  */
 @Description("Options used to configure Google Cloud Platform project and 
credentials.")
-@SuppressWarnings({
-  "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
 public interface GcpOptions extends GoogleApiDebugOptions, PipelineOptions {
   /** Project id to use when launching jobs. */
   @Description(
@@ -248,11 +245,11 @@ public interface GcpOptions extends 
GoogleApiDebugOptions, PipelineOptions {
    * Attempts to infer the default project based upon the environment this 
application is executing
    * within. Currently this only supports getting the active project from 
gcloud.
    */
-  class DefaultProjectFactory implements DefaultValueFactory<String> {
+  class DefaultProjectFactory implements DefaultValueFactory<@Nullable String> 
{
     private static final Logger LOG = 
LoggerFactory.getLogger(DefaultProjectFactory.class);
 
     @Override
-    public String create(PipelineOptions options) {
+    public @Nullable String create(PipelineOptions options) {
       try {
         File configFile;
         if (getEnvironment().containsKey("CLOUDSDK_CONFIG")) {
@@ -293,7 +290,7 @@ public interface GcpOptions extends GoogleApiDebugOptions, 
PipelineOptions {
           } else if (section == null || "core".equals(section)) {
             matcher = projectPattern.matcher(line);
             if (matcher.matches()) {
-              String project = matcher.group(1).trim();
+              String project = checkArgumentNotNull(matcher.group(1)).trim();
               LOG.info(
                   "Inferred default GCP project '{}' from gcloud. If this is 
the incorrect "
                       + "project, please cancel this Pipeline and specify the 
command-line "
@@ -326,9 +323,9 @@ public interface GcpOptions extends GoogleApiDebugOptions, 
PipelineOptions {
    * Attempts to load the GCP credentials. See {@link 
CredentialFactory#getCredential()} for more
    * details.
    */
-  class GcpUserCredentialsFactory implements DefaultValueFactory<Credentials> {
+  class GcpUserCredentialsFactory implements DefaultValueFactory<@Nullable 
Credentials> {
     @Override
-    public Credentials create(PipelineOptions options) {
+    public @Nullable Credentials create(PipelineOptions options) {
       GcpOptions gcpOptions = options.as(GcpOptions.class);
       try {
         CredentialFactory factory =
@@ -365,7 +362,7 @@ public interface GcpOptions extends GoogleApiDebugOptions, 
PipelineOptions {
   void setGcpTempLocation(String value);
 
   /** Returns {@link PipelineOptions#getTempLocation} as the default GCP temp 
location. */
-  class GcpTempLocationFactory implements DefaultValueFactory<String> {
+  class GcpTempLocationFactory implements DefaultValueFactory<@Nullable 
String> {
     private static final FluentBackoff BACKOFF_FACTORY =
         
FluentBackoff.DEFAULT.withMaxRetries(3).withInitialBackoff(Duration.millis(200));
     static final String DEFAULT_REGION = "us-central1";
@@ -415,7 +412,7 @@ public interface GcpOptions extends GoogleApiDebugOptions, 
PipelineOptions {
       GcsUtil gcsUtil = gcsOptions.getGcsUtil();
       try {
         SoftDeletePolicy policy =
-            
Objects.requireNonNull(gcsUtil.getBucket(GcsPath.fromUri(tempLocation)))
+            
checkArgumentNotNull(gcsUtil.getBucket(GcsPath.fromUri(tempLocation)))
                 .getSoftDeletePolicy();
         if (policy != null && policy.getRetentionDurationSeconds() > 0) {
           return true;
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/options/GoogleApiDebugOptions.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/options/GoogleApiDebugOptions.java
index 1577b9a9d08..348e59d2b79 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/options/GoogleApiDebugOptions.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/options/GoogleApiDebugOptions.java
@@ -17,6 +17,8 @@
  */
 package org.apache.beam.sdk.extensions.gcp.options;
 
+import static org.apache.beam.sdk.util.Preconditions.checkArgumentNotNull;
+
 import com.google.api.client.googleapis.services.AbstractGoogleClient;
 import com.google.api.client.googleapis.services.AbstractGoogleClientRequest;
 import 
com.google.api.client.googleapis.services.GoogleClientRequestInitializer;
@@ -25,13 +27,11 @@ import java.util.HashMap;
 import java.util.Map;
 import org.apache.beam.sdk.options.Description;
 import org.apache.beam.sdk.options.PipelineOptions;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * These options configure debug settings for Google API clients created 
within the Apache Beam SDK.
  */
-@SuppressWarnings({
-  "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
 public interface GoogleApiDebugOptions extends PipelineOptions {
   /**
    * This option enables tracing of API calls to Google services used within 
the Apache Beam SDK.
@@ -67,7 +67,7 @@ public interface GoogleApiDebugOptions extends 
PipelineOptions {
      * given client type.
      */
     public GoogleApiTracer addTraceFor(AbstractGoogleClient client, String 
traceDestination) {
-      put(client.getClass().getCanonicalName(), traceDestination);
+      put(checkArgumentNotNull(client.getClass().getCanonicalName()), 
traceDestination);
       return this;
     }
 
@@ -77,14 +77,15 @@ public interface GoogleApiDebugOptions extends 
PipelineOptions {
      */
     public GoogleApiTracer addTraceFor(
         AbstractGoogleClientRequest<?> request, String traceDestination) {
-      put(request.getClass().getCanonicalName(), traceDestination);
+      put(checkArgumentNotNull(request.getClass().getCanonicalName()), 
traceDestination);
       return this;
     }
 
     @Override
     public void initialize(AbstractGoogleClientRequest<?> request) throws 
IOException {
+      @Nullable String requestClassName = 
request.getClass().getCanonicalName();
       for (Map.Entry<String, String> entry : this.entrySet()) {
-        if (request.getClass().getCanonicalName().contains(entry.getKey())) {
+        if (requestClassName != null && 
requestClassName.contains(entry.getKey())) {
           request.set("$trace", entry.getValue());
         }
       }
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/storage/GcsFileSystem.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/storage/GcsFileSystem.java
index 5eca4e9e2c2..319ff4ce802 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/storage/GcsFileSystem.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/storage/GcsFileSystem.java
@@ -18,6 +18,8 @@
 package org.apache.beam.sdk.extensions.gcp.storage;
 
 import static org.apache.beam.sdk.io.FileSystemUtils.wildcardToRegexp;
+import static org.apache.beam.sdk.util.Preconditions.checkArgumentNotNull;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.MoreObjects.firstNonNull;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkNotNull;
@@ -69,9 +71,6 @@ import org.slf4j.LoggerFactory;
  * 
href="https://github.com/apache/beam/blob/master/sdks/java/extensions/google-cloud-platform-core/OWNERS";>
  * here</a>.
  */
-@SuppressWarnings({
-  "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
 class GcsFileSystem extends FileSystem<GcsResourceId> {
   private static final Logger LOG = 
LoggerFactory.getLogger(GcsFileSystem.class);
 
@@ -183,7 +182,7 @@ class GcsFileSystem extends FileSystem<GcsResourceId> {
     if (createOptions instanceof GcsCreateOptions) {
       builder =
           builder.setUploadBufferSizeBytes(
-              ((GcsCreateOptions) createOptions).gcsUploadBufferSizeBytes());
+              checkArgumentNotNull(((GcsCreateOptions) 
createOptions).gcsUploadBufferSizeBytes()));
     }
     Stopwatch stopwatch = Stopwatch.createStarted();
     try {
@@ -376,8 +375,9 @@ class GcsFileSystem extends FileSystem<GcsResourceId> {
     } else if (exception != null) {
       return MatchResult.create(Status.ERROR, exception);
     } else {
-      StorageObject object = objectOrException.storageObject();
-      assert object != null; // fix a warning; guaranteed by 
StorageObjectOrIOException semantics.
+      // Guaranteed non-null by StorageObjectOrIOException semantics: exactly 
one of
+      // storageObject/ioException is set, and ioException was null above.
+      StorageObject object = 
checkStateNotNull(objectOrException.storageObject());
       return MatchResult.create(Status.OK, 
ImmutableList.of(toMetadata(object)));
     }
   }
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/storage/GcsResourceId.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/storage/GcsResourceId.java
index 84582277467..eac46f4d3f2 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/storage/GcsResourceId.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/storage/GcsResourceId.java
@@ -17,6 +17,7 @@
  */
 package org.apache.beam.sdk.extensions.gcp.storage;
 
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkNotNull;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkState;
@@ -72,10 +73,9 @@ public class GcsResourceId implements ResourceId {
     if (isDirectory()) {
       return this;
     } else {
-      GcsPath parent = gcsPath.getParent();
-      checkState(
-          parent != null,
-          String.format("Failed to get the current directory for path: [%s].", 
gcsPath));
+      GcsPath parent =
+          checkStateNotNull(
+              gcsPath.getParent(), "Failed to get the current directory for 
path: [%s].", gcsPath);
       return fromGcsPath(parent);
     }
   }
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/CustomHttpErrors.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/CustomHttpErrors.java
index a70ed3a253b..beb5d889956 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/CustomHttpErrors.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/CustomHttpErrors.java
@@ -21,6 +21,7 @@ import com.google.auto.value.AutoValue;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * An optional component to use with the {@code RetryHttpRequestInitializer} 
in order to provide
@@ -63,9 +64,6 @@ import java.util.List;
  *
  * <p>
  */
-@SuppressWarnings({
-  "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
 public class CustomHttpErrors {
 
   /**
@@ -133,7 +131,7 @@ public class CustomHttpErrors {
   }
 
   /** Returns the first custom error for the failing request and response to 
match, or null. */
-  public String getCustomError(HttpRequestWrapper req, HttpResponseWrapper 
res) {
+  public @Nullable String getCustomError(HttpRequestWrapper req, 
HttpResponseWrapper res) {
     for (MatcherAndError m : matchersAndLogs) {
       if (m.getMatcher().matchResponse(req, res)) {
         return m.getCustomError().customError(req, res);
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcpSecret.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcpSecret.java
index 08329830599..0822dda3127 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcpSecret.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcpSecret.java
@@ -108,6 +108,8 @@ public class GcpSecret extends Secret {
     try {
       Class<?> clazz = Class.forName("com.google.cloud.ServiceOptions");
       java.lang.reflect.Method method = clazz.getMethod("getDefaultProjectId");
+      // getDefaultProjectId is static, so passing a null receiver to 
Method.invoke is correct;
+      // the checker's stub for Method.invoke conservatively requires a 
non-null receiver.
       @SuppressWarnings("nullness")
       Object result = method.invoke(null);
       if (result != null && !Strings.isNullOrEmpty(result.toString())) {
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcsUtilV1.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcsUtilV1.java
index a04f688ec7a..51f211f36b5 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcsUtilV1.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcsUtilV1.java
@@ -19,6 +19,7 @@ package org.apache.beam.sdk.extensions.gcp.util;
 
 import static org.apache.beam.sdk.io.FileSystemUtils.wildcardToRegexp;
 import static org.apache.beam.sdk.options.ExperimentalOptions.hasExperiment;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkNotNull;
 
@@ -110,9 +111,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /** Provides operations on GCS. */
-@SuppressWarnings({
-  "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
 class GcsUtilV1 {
 
   /** Describes which GCS counters this {@link GcsUtilV1} emits. */
@@ -290,8 +288,12 @@ class GcsUtilV1 {
     }
     googleCloudStorageOptions = optionsBuilder.build();
     try {
-      googleCloudStorage =
+      // createGoogleCloudStorage is an overridable instance method; the 
checker flags calling it on
+      // a still-initializing receiver, but every field it reads is already 
assigned above.
+      @SuppressWarnings("nullness")
+      GoogleCloudStorage gcs =
           createGoogleCloudStorage(googleCloudStorageOptions, storageClient, 
credentials);
+      googleCloudStorage = gcs;
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
@@ -301,6 +303,10 @@ class GcsUtilV1 {
           // are used.
           GcsUtilV1 util = this;
           return new BatchInterface() {
+            // The supplier reads storageClient/httpRequestInitializer through 
the captured
+            // instance when invoked, well after construction completes; the 
checker conservatively
+            // treats the captured receiver as still-initializing.
+            @SuppressWarnings("nullness")
             final BatchRequest batch = 
util.storageClient.batch(util.httpRequestInitializer);
 
             @Override
@@ -477,7 +483,9 @@ class GcsUtilV1 {
     Storage.Objects.List listObject = storageClient.objects().list(bucket);
     listObject.setMaxResults(MAX_LIST_ITEMS_PER_CALL);
     listObject.setPrefix(prefix);
-    listObject.setDelimiter(delimiter);
+    if (delimiter != null) {
+      listObject.setDelimiter(delimiter);
+    }
 
     if (pageToken != null) {
       listObject.setPageToken(pageToken);
@@ -509,10 +517,13 @@ class GcsUtilV1 {
 
   private Long toFileSize(StorageObjectOrIOException 
storageObjectOrIOException)
       throws IOException {
-    if (storageObjectOrIOException.ioException() != null) {
-      throw storageObjectOrIOException.ioException();
+    IOException ioException = storageObjectOrIOException.ioException();
+    if (ioException != null) {
+      throw ioException;
     } else {
-      return storageObjectOrIOException.storageObject().getSize().longValue();
+      // Exactly one of storageObject/ioException is set; ioException was null 
above.
+      StorageObject storageObject = 
checkStateNotNull(storageObjectOrIOException.storageObject());
+      return checkStateNotNull(storageObject.getSize()).longValue();
     }
   }
 
@@ -788,8 +799,9 @@ class GcsUtilV1 {
             options.getExpectFileToNotExist() ? 0L : 
StorageResourceId.UNKNOWN_GENERATION_ID);
     CreateObjectOptions.Builder createBuilder =
         CreateObjectOptions.builder().setOverwriteExisting(true);
-    if (options.getContentType() != null) {
-      createBuilder = createBuilder.setContentType(options.getContentType());
+    @Nullable String contentType = options.getContentType();
+    if (contentType != null) {
+      createBuilder = createBuilder.setContentType(contentType);
     }
 
     HashMap<String, String> baseLabels = new HashMap<>();
@@ -868,7 +880,9 @@ class GcsUtilV1 {
    * exist, an exception will be thrown.
    */
   public long bucketOwner(GcsPath path) throws IOException {
-    return getBucket(path, createBackOff(), 
Sleeper.DEFAULT).getProjectNumber().longValue();
+    // getBucket throws (rather than returning null) when the bucket is 
missing or inaccessible.
+    Bucket bucket = checkStateNotNull(getBucket(path, createBackOff(), 
Sleeper.DEFAULT));
+    return checkStateNotNull(bucket.getProjectNumber()).longValue();
   }
 
   /**
@@ -880,8 +894,7 @@ class GcsUtilV1 {
   }
 
   /** Get the {@link Bucket} from Cloud Storage path or propagates an 
exception. */
-  @Nullable
-  public Bucket getBucket(GcsPath path) throws IOException {
+  public @Nullable Bucket getBucket(GcsPath path) throws IOException {
     return getBucket(path, createBackOff(), Sleeper.DEFAULT);
   }
 
@@ -913,6 +926,27 @@ class GcsUtilV1 {
     getBucket(path, backoff, sleeper);
   }
 
+  // The JDK constructor stubs for these exceptions aren't annotated for 
nullness, but a null
+  // "other" file argument is permitted. Concentrating the suppression in 
these helpers.
+  @SuppressWarnings("nullness")
+  private static AccessDeniedException createAccessDeniedException(
+      String file, @Nullable String reason) {
+    return new AccessDeniedException(file, null, reason);
+  }
+
+  @SuppressWarnings("nullness")
+  private static FileAlreadyExistsException createFileAlreadyExistsException(
+      String file, @Nullable String reason) {
+    return new FileAlreadyExistsException(file, null, reason);
+  }
+
+  // FileNotFoundException's message argument accepts null, but the JDK 
constructor stub isn't
+  // annotated for nullness.
+  @SuppressWarnings("nullness")
+  private static FileNotFoundException createFileNotFoundException(@Nullable 
String message) {
+    return new FileNotFoundException(message);
+  }
+
   @VisibleForTesting
   @Nullable Bucket getBucket(GcsPath path, BackOff backoff, Sleeper sleeper) 
throws IOException {
     Storage.Buckets.Get getBucket = 
storageClient.buckets().get(path.getBucket());
@@ -934,10 +968,10 @@ class GcsUtilV1 {
           sleeper);
     } catch (GoogleJsonResponseException e) {
       if (errorExtractor.accessDenied(e)) {
-        throw new AccessDeniedException(path.toString(), null, e.getMessage());
+        throw createAccessDeniedException(path.toString(), e.getMessage());
       }
       if (errorExtractor.itemNotFound(e)) {
-        throw new FileNotFoundException(e.getMessage());
+        throw createFileNotFoundException(e.getMessage());
       }
       throw e;
     } catch (InterruptedException e) {
@@ -974,10 +1008,10 @@ class GcsUtilV1 {
       return;
     } catch (GoogleJsonResponseException e) {
       if (errorExtractor.accessDenied(e)) {
-        throw new AccessDeniedException(bucket.getName(), null, 
e.getMessage());
+        throw createAccessDeniedException(bucket.getName(), e.getMessage());
       }
       if (errorExtractor.itemAlreadyExists(e)) {
-        throw new FileAlreadyExistsException(bucket.getName(), null, 
e.getMessage());
+        throw createFileAlreadyExistsException(bucket.getName(), 
e.getMessage());
       }
       throw e;
     } catch (InterruptedException e) {
@@ -1011,10 +1045,10 @@ class GcsUtilV1 {
           sleeper);
     } catch (GoogleJsonResponseException e) {
       if (errorExtractor.accessDenied(e)) {
-        throw new AccessDeniedException(bucket.getName(), null, 
e.getMessage());
+        throw createAccessDeniedException(bucket.getName(), e.getMessage());
       }
       if (errorExtractor.itemNotFound(e)) {
-        throw new FileNotFoundException(e.getMessage());
+        throw createFileNotFoundException(e.getMessage());
       }
       throw e;
     } catch (InterruptedException e) {
@@ -1177,6 +1211,9 @@ class GcsUtilV1 {
           });
     }
 
+    // Storage.Objects.rewrite permits a null content body (no object-metadata 
change); the
+    // generated client stub is not annotated for nullness.
+    @SuppressWarnings("nullness")
     public RewriteOp(GcsPath from, GcsPath to, boolean deleteSource, boolean 
ignoreMissingSource)
         throws IOException {
       this.from = from;
@@ -1237,8 +1274,10 @@ class GcsUtilV1 {
           && e.getErrors().size() == 1
           && e.getErrors().get(0).getReason().equals("retentionPolicyNotMet")) 
{
         List<StorageObjectOrIOException> srcAndDestObjects = 
getObjects(Arrays.asList(from, to));
-        String srcHash = srcAndDestObjects.get(0).storageObject().getMd5Hash();
-        String destHash = 
srcAndDestObjects.get(1).storageObject().getMd5Hash();
+        @Nullable String srcHash =
+            
checkStateNotNull(srcAndDestObjects.get(0).storageObject()).getMd5Hash();
+        @Nullable String destHash =
+            
checkStateNotNull(srcAndDestObjects.get(1).storageObject()).getMd5Hash();
         if (srcHash != null && srcHash.equals(destHash)) {
           // Source and destination are identical. Treat this as a successful 
rewrite
           LOG.warn(
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcsUtilV2.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcsUtilV2.java
index 9119dd79652..bbdab0982b5 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcsUtilV2.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcsUtilV2.java
@@ -100,10 +100,10 @@ class GcsUtilV2 {
     uploadBufferSizeBytes = 
options.as(GcsOptions.class).getGcsUploadBufferSizeBytes();
   }
 
-  @SuppressWarnings({
-    "nullness" // For Creating AccessDeniedException FileNotFoundException, and
-    // FileAlreadyExistsException with null.
-  })
+  // AccessDeniedException/FileAlreadyExistsException permit a null "other" 
argument, and these
+  // exceptions permit a null detail message, but the JDK constructor stubs 
are not annotated for
+  // nullness.
+  @SuppressWarnings("nullness")
   private static IOException translateStorageException(GcsPath gcsPath, 
StorageException e) {
     switch (e.getCode()) {
       case 403:
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/RetryHttpRequestInitializer.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/RetryHttpRequestInitializer.java
index e3d4eb6ee36..13462db2e3a 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/RetryHttpRequestInitializer.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/RetryHttpRequestInitializer.java
@@ -45,9 +45,6 @@ import org.slf4j.LoggerFactory;
  *
  * <p>Also can take an HttpResponseInterceptor to be applied to the responses.
  */
-@SuppressWarnings({
-  "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
 public class RetryHttpRequestInitializer implements HttpRequestInitializer {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(RetryHttpRequestInitializer.class);
@@ -198,9 +195,10 @@ public class RetryHttpRequestInitializer implements 
HttpRequestInitializer {
     }
   }
 
-  private final HttpResponseInterceptor responseInterceptor; // response 
Interceptor to use
+  private final @Nullable HttpResponseInterceptor
+      responseInterceptor; // response Interceptor to use
 
-  private CustomHttpErrors customHttpErrors = null;
+  private @Nullable CustomHttpErrors customHttpErrors = null;
 
   private final NanoClock nanoClock; // used for testing
 
@@ -208,7 +206,7 @@ public class RetryHttpRequestInitializer implements 
HttpRequestInitializer {
 
   private Set<Integer> ignoredResponseCodes = new 
HashSet<>(DEFAULT_IGNORED_RESPONSE_CODES);
 
-  private Map<String, String> httpHeaders = null;
+  private @Nullable Map<String, String> httpHeaders = null;
 
   public RetryHttpRequestInitializer() {
     this(Collections.emptyList());
@@ -242,7 +240,7 @@ public class RetryHttpRequestInitializer implements 
HttpRequestInitializer {
       NanoClock nanoClock,
       Sleeper sleeper,
       Collection<Integer> additionalIgnoredResponseCodes,
-      HttpResponseInterceptor responseInterceptor) {
+      @Nullable HttpResponseInterceptor responseInterceptor) {
     this.nanoClock = nanoClock;
     this.sleeper = sleeper;
     this.ignoredResponseCodes.addAll(additionalIgnoredResponseCodes);
@@ -273,8 +271,9 @@ public class RetryHttpRequestInitializer implements 
HttpRequestInitializer {
     request.setUnsuccessfulResponseHandler(loggingHttpBackOffHandler);
     request.setIOExceptionHandler(loggingHttpBackOffHandler);
 
-    if (this.httpHeaders != null) {
-      request.getHeaders().putAll(this.httpHeaders);
+    @Nullable Map<String, String> localHttpHeaders = this.httpHeaders;
+    if (localHttpHeaders != null) {
+      request.getHeaders().putAll(localHttpHeaders);
     }
 
     // Set response initializer
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/UploadIdResponseInterceptor.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/UploadIdResponseInterceptor.java
index 8610b7f5f76..7baad4a60e6 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/UploadIdResponseInterceptor.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/UploadIdResponseInterceptor.java
@@ -29,9 +29,6 @@ import org.slf4j.LoggerFactory;
  * is the first request (does not have upload_id parameter in the request). 
Only logs if debug level
  * is enabled.
  */
-@SuppressWarnings({
-  "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
 public class UploadIdResponseInterceptor implements HttpResponseInterceptor {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(UploadIdResponseInterceptor.class);
diff --git 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/gcsfs/GcsPath.java
 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/gcsfs/GcsPath.java
index e242bd136b1..67a78c88f16 100644
--- 
a/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/gcsfs/GcsPath.java
+++ 
b/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/gcsfs/GcsPath.java
@@ -17,6 +17,8 @@
  */
 package org.apache.beam.sdk.extensions.gcp.util.gcsfs;
 
+import static org.apache.beam.sdk.util.Preconditions.checkArgumentNotNull;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Strings.isNullOrEmpty;
 
@@ -65,9 +67,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
  * @see <a href= 
"http://docs.oracle.com/javase/tutorial/essential/io/pathOps.html"; >Java 
Tutorials:
  *     Path Operations</a>
  */
-@SuppressWarnings({
-  "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
 public class GcsPath implements Path, Serializable {
 
   public static final String SCHEME = "gs";
@@ -118,7 +117,10 @@ public class GcsPath implements Path, Serializable {
     Matcher m = GCS_URI.matcher(uri);
     checkArgument(m.matches(), "Invalid GCS URI: %s", uri);
 
-    checkArgument(m.group("SCHEME").equalsIgnoreCase(SCHEME), "URI: %s is not 
a GCS URI", uri);
+    checkArgument(
+        checkArgumentNotNull(m.group("SCHEME")).equalsIgnoreCase(SCHEME),
+        "URI: %s is not a GCS URI",
+        uri);
     return new GcsPath(null, m.group("BUCKET"), m.group("OBJECT"));
   }
 
@@ -233,7 +235,10 @@ public class GcsPath implements Path, Serializable {
   }
 
   @Override
-  public FileSystem getFileSystem() {
+  // fs is null for paths not attached to a filesystem. @Nullable documents 
the contract; the
+  // suppression covers overriding Path.getFileSystem, which the checker 
treats as @NonNull.
+  @SuppressWarnings("nullness")
+  public @Nullable FileSystem getFileSystem() {
     return fs;
   }
 
@@ -264,7 +269,7 @@ public class GcsPath implements Path, Serializable {
    * <p>Returns a path that ends in '/', as the parent path always refers to a 
directory.
    */
   @Override
-  public GcsPath getParent() {
+  public @Nullable GcsPath getParent() {
     if (bucket.isEmpty() && object.isEmpty()) {
       // The root path has no parent, by definition.
       return null;
@@ -341,7 +346,8 @@ public class GcsPath implements Path, Serializable {
       ++beginIndex;
     }
 
-    return path;
+    // endIndex > beginIndex is checked above, so the loop runs at least once 
and path is set.
+    return checkStateNotNull(path);
   }
 
   @Override
@@ -403,7 +409,10 @@ public class GcsPath implements Path, Serializable {
 
     if (other.startsWith(SCHEME + "://")) {
       GcsPath path = GcsPath.fromUri(other);
-      path.setFileSystem(getFileSystem());
+      @Nullable FileSystem currentFs = getFileSystem();
+      if (currentFs != null) {
+        path.setFileSystem(currentFs);
+      }
       return path;
     }
 
@@ -473,11 +482,11 @@ public class GcsPath implements Path, Serializable {
   }
 
   private static class NameIterator implements Iterator<Path> {
-    private final FileSystem fs;
+    private final @Nullable FileSystem fs;
     private boolean fullPath;
-    private String name;
+    private @Nullable String name;
 
-    NameIterator(FileSystem fs, boolean fullPath, String name) {
+    NameIterator(@Nullable FileSystem fs, boolean fullPath, String name) {
       this.fs = fs;
       this.fullPath = fullPath;
       this.name = name;
@@ -490,13 +499,14 @@ public class GcsPath implements Path, Serializable {
 
     @Override
     public GcsPath next() {
-      int i = name.indexOf('/');
+      String currentName = checkStateNotNull(name);
+      int i = currentName.indexOf('/');
       String component;
       if (i >= 0) {
-        component = name.substring(0, i);
-        name = name.substring(i + 1);
+        component = currentName.substring(0, i);
+        name = currentName.substring(i + 1);
       } else {
-        component = name;
+        component = currentName;
         name = null;
       }
       if (fullPath) {
@@ -593,6 +603,8 @@ public class GcsPath implements Path, Serializable {
   }
 
   @Override
+  // URI permits a null fragment; the JDK URI constructor stub is not 
annotated for nullness.
+  @SuppressWarnings("nullness")
   public URI toUri() {
     try {
       return new URI(SCHEME, "//" + bucketAndObject(), null);
@@ -613,7 +625,7 @@ public class GcsPath implements Path, Serializable {
   public static String getNonWildcardPrefix(String globExp) {
     Matcher m = GLOB_PREFIX.matcher(globExp);
     checkArgument(m.matches(), String.format("Glob expression: [%s] is not 
expandable.", globExp));
-    return m.group("PREFIX");
+    return checkArgumentNotNull(m.group("PREFIX"));
   }
 
   /** Returns true if the given {@code spec} contains wildcard. */

Reply via email to