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