diff --git a/google-cloud-storage/clirr-ignored-differences.xml b/google-cloud-storage/clirr-ignored-differences.xml
index d227206843..cc9d69330f 100644
--- a/google-cloud-storage/clirr-ignored-differences.xml
+++ b/google-cloud-storage/clirr-ignored-differences.xml
@@ -71,4 +71,11 @@
com/google/cloud/storage/transfermanager/TransferManagerConfig$Builder
* setAllowDivideAndConquer(boolean)
+
+
+ 7013
+ com/google/cloud/storage/StorageOptions$Builder
+ com.google.cloud.storage.StorageOptions$Builder setBlobWriteSessionConfig(com.google.cloud.storage.BlobWriteSessionConfig)
+
+
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteSession.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteSession.java
index 02ea23a6a7..81cecdc892 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteSession.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteSession.java
@@ -20,6 +20,7 @@
import com.google.api.core.BetaApi;
import java.io.IOException;
import java.nio.channels.WritableByteChannel;
+import java.util.concurrent.TimeUnit;
/**
* A session to write an object to Google Cloud Storage.
@@ -50,6 +51,10 @@ public interface BlobWriteSession {
*
Upon calling {@link WritableByteChannel#close()} the object creation will be finalized, and
* {@link #getResult()}s future should resolve.
*
+ *
The returned {@code WritableByteChannel} can throw IOExceptions from any of its usual
+ * methods. Any {@link IOException} thrown can have a cause of a {@link StorageException}.
+ * However, not all {@code IOExceptions} will have {@code StorageException}s.
+ *
* @throws IOException When creating the {@link WritableByteChannel} if an unrecoverable
* underlying IOException occurs it can be rethrown
* @throws IllegalStateException if open is called more than once
@@ -66,6 +71,10 @@ public interface BlobWriteSession {
* Google Cloud Storage 2. A terminal failure occurs, the terminal failure will become the
* exception result
*
+ *
If a terminal failure is encountered, calling either {@link ApiFuture#get()} or {@link
+ * ApiFuture#get(long, TimeUnit)} will result in an {@link
+ * java.util.concurrent.ExecutionException} with a cause that is the {@link StorageException}.
+ *
* @since 2.26.0 This new api is in preview and is subject to breaking changes.
*/
@BetaApi
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteSessions.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteSessions.java
index 878552a125..c9da9ee05c 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteSessions.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteSessions.java
@@ -17,6 +17,7 @@
package com.google.cloud.storage;
import com.google.api.core.ApiFuture;
+import com.google.common.base.Preconditions;
import java.io.IOException;
import java.nio.channels.WritableByteChannel;
@@ -30,14 +31,20 @@ static BlobWriteSession of(WritableByteChannelSession, BlobInfo> s) {
static final class WritableByteChannelSessionAdapter implements BlobWriteSession {
private final WritableByteChannelSession, BlobInfo> delegate;
+ private boolean open;
private WritableByteChannelSessionAdapter(WritableByteChannelSession, BlobInfo> delegate) {
this.delegate = delegate;
+ open = false;
}
@Override
public WritableByteChannel open() throws IOException {
- return delegate.open();
+ synchronized (this) {
+ Preconditions.checkState(!open, "already open");
+ open = true;
+ return delegate.open();
+ }
}
@Override
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/DefaultBlobWriteSessionConfig.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/DefaultBlobWriteSessionConfig.java
index 1d45c9e34c..ffcbcfa8ee 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/DefaultBlobWriteSessionConfig.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/DefaultBlobWriteSessionConfig.java
@@ -32,6 +32,8 @@
import com.google.common.util.concurrent.MoreExecutors;
import com.google.storage.v2.WriteObjectRequest;
import com.google.storage.v2.WriteObjectResponse;
+import java.io.IOException;
+import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.time.Clock;
import java.util.Map;
@@ -202,24 +204,25 @@ static final class DecoratedWritableByteChannelSession openAsync() {
- return delegate.openAsync();
+ return ApiFutures.catchingAsync(
+ delegate.openAsync(),
+ Throwable.class,
+ throwable -> ApiFutures.immediateFailedFuture(StorageException.coalesce(throwable)),
+ MoreExecutors.directExecutor());
}
@Override
public ApiFuture getResult() {
- return ApiFutures.transform(
- delegate.getResult(), decoder::decode, MoreExecutors.directExecutor());
+ ApiFuture decodeResult =
+ ApiFutures.transform(
+ delegate.getResult(), decoder::decode, MoreExecutors.directExecutor());
+ return ApiFutures.catchingAsync(
+ decodeResult,
+ Throwable.class,
+ throwable -> ApiFutures.immediateFailedFuture(StorageException.coalesce(throwable)),
+ MoreExecutors.directExecutor());
}
}
@@ -233,7 +236,55 @@ static final class LazySession
@Override
public ApiFuture openAsync() {
- return lazy.getSession().openAsync();
+ // make sure the errors coming out of the BufferedWritableByteChannel are either IOException
+ // or StorageException
+ return ApiFutures.transform(
+ lazy.getSession().openAsync(),
+ delegate ->
+ new BufferedWritableByteChannel() {
+ @Override
+ public int write(ByteBuffer src) throws IOException {
+ try {
+ return delegate.write(src);
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw StorageException.coalesce(e);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ try {
+ delegate.flush();
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw StorageException.coalesce(e);
+ }
+ }
+
+ @Override
+ public boolean isOpen() {
+ try {
+ return delegate.isOpen();
+ } catch (Exception e) {
+ throw StorageException.coalesce(e);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ delegate.close();
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw StorageException.coalesce(e);
+ }
+ }
+ },
+ MoreExecutors.directExecutor());
}
@Override
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannel.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannel.java
index b4a26ed4d3..32d5eb9dc8 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannel.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannel.java
@@ -92,7 +92,12 @@ public void close() throws IOException {
throw e;
}
} else {
- flusher.close(null);
+ try {
+ flusher.close(null);
+ } catch (RuntimeException e) {
+ resultFuture.setException(e);
+ throw e;
+ }
}
open = false;
}
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUploadSessionBuilder.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUploadSessionBuilder.java
index ac52021308..2d1daa5444 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUploadSessionBuilder.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUploadSessionBuilder.java
@@ -49,7 +49,7 @@ GapicBidiWritableByteChannelSessionBuilder bidiByteChannel(
}
ApiFuture resumableWrite(
- UnaryCallable x,
+ UnaryCallable callable,
WriteObjectRequest writeObjectRequest) {
StartResumableWriteRequest.Builder b = StartResumableWriteRequest.newBuilder();
if (writeObjectRequest.hasWriteObjectSpec()) {
@@ -65,9 +65,16 @@ ApiFuture resumableWrite(
Function f =
uploadId ->
writeObjectRequest.toBuilder().clearWriteObjectSpec().setUploadId(uploadId).build();
- return ApiFutures.transform(
- x.futureCall(req),
- (resp) -> new ResumableWrite(req, resp, f),
+ ApiFuture futureResumableWrite =
+ ApiFutures.transform(
+ callable.futureCall(req),
+ (resp) -> new ResumableWrite(req, resp, f),
+ MoreExecutors.directExecutor());
+ // make sure we wrap any failure as a storage exception
+ return ApiFutures.catchingAsync(
+ futureResumableWrite,
+ Throwable.class,
+ throwable -> ApiFutures.immediateFailedFuture(StorageException.coalesce(throwable)),
MoreExecutors.directExecutor());
}
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java
index 192cfec6dd..622988d4cb 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java
@@ -120,7 +120,7 @@ public void rewindTo(long offset) {
}
} else if (finalizing && JsonResumableSessionFailureScenario.isOk(code)) {
@Nullable StorageObject storageObject;
- @Nullable BigInteger actualSize;
+ BigInteger actualSize = BigInteger.ZERO;
Long contentLength = response.getHeaders().getContentLength();
String contentType = response.getHeaders().getContentType();
@@ -130,7 +130,12 @@ public void rewindTo(long offset) {
boolean isJson = contentType != null && contentType.startsWith("application/json");
if (isJson) {
storageObject = response.parseAs(StorageObject.class);
- actualSize = storageObject != null ? storageObject.getSize() : null;
+ if (storageObject != null) {
+ BigInteger size = storageObject.getSize();
+ if (size != null) {
+ actualSize = size;
+ }
+ }
} else if ((contentLength == null || contentLength == 0) && storedContentLength != null) {
// when a signed url is used, the finalize response is empty
response.ignore();
@@ -150,7 +155,6 @@ public void rewindTo(long offset) {
int compare = expectedSize.compareTo(actualSize);
if (compare == 0) {
success = true;
- //noinspection DataFlowIssue compareTo result will filter out actualSize == null
return ResumableOperationResult.complete(storageObject, actualSize.longValue());
} else if (compare > 0) {
StorageException se =
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageException.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageException.java
index f8b4f94a8d..eeb24a44b8 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageException.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageException.java
@@ -118,6 +118,9 @@ static BaseServiceException coalesce(Throwable t) {
if (t instanceof ApiException) {
return asStorageException((ApiException) t);
}
+ if (t.getCause() instanceof ApiException) {
+ return asStorageException((ApiException) t.getCause());
+ }
return getStorageException(t);
}
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageOptions.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageOptions.java
index 53ad6142e9..ab32532ae1 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageOptions.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageOptions.java
@@ -22,14 +22,17 @@
import com.google.cloud.ServiceDefaults;
import com.google.cloud.ServiceOptions;
import com.google.cloud.http.HttpTransportOptions;
+import com.google.cloud.storage.GrpcStorageOptions.GrpcStorageDefaults;
import com.google.cloud.storage.HttpStorageOptions.HttpStorageDefaults;
import com.google.cloud.storage.HttpStorageOptions.HttpStorageFactory;
import com.google.cloud.storage.HttpStorageOptions.HttpStorageRpcFactory;
+import com.google.cloud.storage.Storage.BlobWriteOption;
import com.google.cloud.storage.TransportCompatibility.Transport;
import com.google.cloud.storage.spi.StorageRpcFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
+import org.checkerframework.checker.nullness.qual.NonNull;
public abstract class StorageOptions extends ServiceOptions {
@@ -95,6 +98,18 @@ public abstract static class Builder
public abstract Builder setStorageRetryStrategy(StorageRetryStrategy storageRetryStrategy);
+ /**
+ * @see BlobWriteSessionConfig
+ * @see BlobWriteSessionConfigs
+ * @see Storage#blobWriteSession(BlobInfo, BlobWriteOption...)
+ * @see HttpStorageDefaults#getDefaultStorageWriterConfig()
+ * @see GrpcStorageDefaults#getDefaultStorageWriterConfig()
+ * @since 2.37.0 This new api is in preview and is subject to breaking changes.
+ */
+ @BetaApi
+ public abstract StorageOptions.Builder setBlobWriteSessionConfig(
+ @NonNull BlobWriteSessionConfig blobWriteSessionConfig);
+
@Override
public abstract StorageOptions build();
}
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteSessionCommonSemanticsTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteSessionCommonSemanticsTest.java
new file mode 100644
index 0000000000..dc02c3fb9b
--- /dev/null
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteSessionCommonSemanticsTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.storage.it;
+
+import static com.google.cloud.storage.TestUtils.assertAll;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.google.cloud.storage.BlobInfo;
+import com.google.cloud.storage.BlobWriteSession;
+import com.google.cloud.storage.BlobWriteSessionConfig;
+import com.google.cloud.storage.BlobWriteSessionConfigs;
+import com.google.cloud.storage.BucketInfo;
+import com.google.cloud.storage.ParallelCompositeUploadBlobWriteSessionConfig.BufferAllocationStrategy;
+import com.google.cloud.storage.ParallelCompositeUploadBlobWriteSessionConfig.ExecutorSupplier;
+import com.google.cloud.storage.ParallelCompositeUploadBlobWriteSessionConfig.PartCleanupStrategy;
+import com.google.cloud.storage.ParallelCompositeUploadBlobWriteSessionConfig.PartMetadataFieldDecorator;
+import com.google.cloud.storage.ParallelCompositeUploadBlobWriteSessionConfig.PartNamingStrategy;
+import com.google.cloud.storage.Storage;
+import com.google.cloud.storage.Storage.BlobTargetOption;
+import com.google.cloud.storage.Storage.BlobWriteOption;
+import com.google.cloud.storage.StorageException;
+import com.google.cloud.storage.StorageOptions;
+import com.google.cloud.storage.TransportCompatibility.Transport;
+import com.google.cloud.storage.it.ITBlobWriteSessionCommonSemanticsTest.ParamsProvider;
+import com.google.cloud.storage.it.runner.StorageITRunner;
+import com.google.cloud.storage.it.runner.annotations.Backend;
+import com.google.cloud.storage.it.runner.annotations.CrossRun;
+import com.google.cloud.storage.it.runner.annotations.Inject;
+import com.google.cloud.storage.it.runner.annotations.Parameterized;
+import com.google.cloud.storage.it.runner.annotations.Parameterized.Parameter;
+import com.google.cloud.storage.it.runner.annotations.Parameterized.ParametersProvider;
+import com.google.cloud.storage.it.runner.registry.Generator;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+
+@RunWith(StorageITRunner.class)
+@CrossRun(
+ backends = {Backend.PROD},
+ transports = {Transport.HTTP, Transport.GRPC})
+@Parameterized(ParamsProvider.class)
+public final class ITBlobWriteSessionCommonSemanticsTest {
+
+ @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Inject public Storage injectedStorage;
+ @Inject public BucketInfo bucket;
+ @Inject public Generator generator;
+
+ @Parameter public Params params;
+
+ private Storage storage;
+
+ @Before
+ public void setUp() throws Exception {
+ Path tmpDir = temporaryFolder.newFolder().toPath();
+ BlobWriteSessionConfig config = params.ctor.apply(tmpDir);
+
+ StorageOptions originalOptions = injectedStorage.getOptions();
+ StorageOptions newOptions = null;
+ try {
+ newOptions = originalOptions.toBuilder().setBlobWriteSessionConfig(config).build();
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessageThat().contains("not compatible with this");
+ assumeTrue(false);
+ }
+ storage = newOptions.getService();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (storage != null) {
+ storage.close();
+ }
+ }
+
+ @Test
+ public void closingAnOpenedSessionWithoutCallingWriteShouldMakeAnEmptyObject()
+ throws IOException, ExecutionException, InterruptedException, TimeoutException {
+ BlobInfo info = BlobInfo.newBuilder(bucket, generator.randomObjectName()).build();
+ BlobWriteSession session = storage.blobWriteSession(info, BlobWriteOption.doesNotExist());
+
+ WritableByteChannel open = session.open();
+ open.close();
+ BlobInfo gen1 = session.getResult().get(1, TimeUnit.SECONDS);
+
+ // sometimes testbench will not define `.size = 0`, default it here if we get null
+ Long size = gen1.getSize();
+ if (size == null) {
+ size = 0L;
+ }
+ assertThat(size).isEqualTo(0);
+ }
+
+ @Test
+ public void attemptingToUseASessionWhichResultsInFailureShouldThrowAStorageException() {
+ // attempt to write to a bucket which we have not created
+ String badBucketName = bucket.getName() + "x";
+ BlobInfo info = BlobInfo.newBuilder(badBucketName, generator.randomObjectName()).build();
+
+ BlobWriteSession session = storage.blobWriteSession(info, BlobWriteOption.doesNotExist());
+ StorageException se =
+ assertThrows(
+ StorageException.class,
+ () -> {
+ WritableByteChannel open = session.open();
+ open.close();
+ });
+
+ assertThat(se.getCode()).isEqualTo(404);
+ }
+
+ @Test
+ public void callingOpenIsOnlyAllowedOnce() throws Exception {
+ BlobInfo info = BlobInfo.newBuilder(bucket, generator.randomObjectName()).build();
+ BlobWriteSession session = storage.blobWriteSession(info, BlobWriteOption.doesNotExist());
+
+ WritableByteChannel open = session.open();
+ IllegalStateException se = assertThrows(IllegalStateException.class, session::open);
+
+ assertAll(() -> assertThat(se.getMessage()).contains("already open"));
+ }
+
+ @Test
+ public void getResultErrorsWhenTheSessionErrors() throws Exception {
+ BlobInfo info = BlobInfo.newBuilder(bucket, generator.randomObjectName()).build();
+ byte[] helloWorld = "Hello World".getBytes(StandardCharsets.UTF_8);
+ storage.create(info, helloWorld, BlobTargetOption.doesNotExist());
+
+ BlobWriteSession session =
+ storage.blobWriteSession(
+ info,
+ // this precondition will result in failure
+ BlobWriteOption.doesNotExist());
+
+ try (WritableByteChannel open = session.open()) {
+ open.write(ByteBuffer.wrap(helloWorld));
+ } catch (StorageException se) {
+ assertThat(se.getCode()).isEqualTo(412);
+ } catch (IOException ioe) {
+ assertThat(ioe).hasCauseThat().isInstanceOf(StorageException.class);
+ StorageException se = (StorageException) ioe.getCause();
+ assertThat(se.getCode()).isEqualTo(412);
+ }
+
+ ExecutionException resultSe =
+ assertThrows(ExecutionException.class, () -> session.getResult().get(10, TimeUnit.SECONDS));
+
+ assertAll(
+ () -> assertThat(resultSe).hasCauseThat().isInstanceOf(StorageException.class),
+ () -> assertThat(((StorageException) resultSe.getCause()).getCode()).isEqualTo(412));
+ }
+
+ public static final class ParamsProvider implements ParametersProvider {
+ @Override
+ public ImmutableList parameters() {
+ final int _2MiB = 2 * 1024 * 1024;
+ final int _4MiB = 4 * 1024 * 1024;
+ return ImmutableList.of(
+ new Params("default", p -> BlobWriteSessionConfigs.getDefault()),
+ new Params("c!c.2MiB", p -> BlobWriteSessionConfigs.getDefault().withChunkSize(_2MiB)),
+ new Params("b!p.1", BlobWriteSessionConfigs::bufferToDiskThenUpload),
+ new Params("j!p.1", p -> BlobWriteSessionConfigs.journaling(ImmutableList.of(p))),
+ new Params(
+ "p!t.c&b.s*&p.4MiB&c.n&m.n",
+ p ->
+ BlobWriteSessionConfigs.parallelCompositeUpload()
+ .withExecutorSupplier(ExecutorSupplier.cachedPool())
+ .withPartNamingStrategy(PartNamingStrategy.noPrefix())
+ .withBufferAllocationStrategy(BufferAllocationStrategy.simple(_4MiB))
+ .withPartCleanupStrategy(PartCleanupStrategy.never())
+ .withPartMetadataFieldDecorator(PartMetadataFieldDecorator.noOp())),
+ new Params("d!c.2MiB", p -> BlobWriteSessionConfigs.bidiWrite().withBufferSize(_2MiB)));
+ }
+ }
+
+ public interface ParamsCtor {
+ BlobWriteSessionConfig apply(Path p) throws IOException;
+ }
+
+ public static final class Params {
+ private final String desc;
+ private final ParamsCtor ctor;
+
+ public Params(String desc, ParamsCtor ctor) {
+ this.desc = desc;
+ this.ctor = ctor;
+ }
+
+ @Override
+ public String toString() {
+ return desc;
+ }
+ }
+}
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteSessionTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteSessionTest.java
index 4abe949498..10ae174a35 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteSessionTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteSessionTest.java
@@ -17,7 +17,6 @@
package com.google.cloud.storage.it;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import com.google.cloud.storage.BlobInfo;
@@ -25,12 +24,10 @@
import com.google.cloud.storage.BlobWriteSessionConfigs;
import com.google.cloud.storage.BucketInfo;
import com.google.cloud.storage.DataGenerator;
-import com.google.cloud.storage.GrpcStorageOptions;
import com.google.cloud.storage.HttpStorageOptions;
import com.google.cloud.storage.JournalingBlobWriteSessionConfig;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.Storage.BlobWriteOption;
-import com.google.cloud.storage.StorageException;
import com.google.cloud.storage.StorageOptions;
import com.google.cloud.storage.TransportCompatibility.Transport;
import com.google.cloud.storage.it.runner.StorageITRunner;
@@ -39,13 +36,10 @@
import com.google.cloud.storage.it.runner.annotations.Inject;
import com.google.cloud.storage.it.runner.registry.Generator;
import com.google.common.collect.ImmutableList;
-import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Path;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -60,7 +54,6 @@ public final class ITBlobWriteSessionTest {
@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
@Inject public Storage storage;
- @Inject public Transport transport;
@Inject public BucketInfo bucket;
@@ -73,22 +66,13 @@ public void allDefaults() throws Exception {
@Test
public void bufferToTempDirThenUpload() throws Exception {
- StorageOptions options = null;
- if (transport == Transport.GRPC) {
- options =
- ((GrpcStorageOptions) storage.getOptions())
- .toBuilder()
- .setBlobWriteSessionConfig(BlobWriteSessionConfigs.bufferToTempDirThenUpload())
- .build();
- } else if (transport == Transport.HTTP) {
- options =
- ((HttpStorageOptions) storage.getOptions())
- .toBuilder()
- .setBlobWriteSessionConfig(BlobWriteSessionConfigs.bufferToTempDirThenUpload())
- .build();
- }
- assertWithMessage("unable to resolve options").that(options).isNotNull();
- //noinspection DataFlowIssue
+ Path path = temporaryFolder.newFolder().toPath();
+ StorageOptions options =
+ storage
+ .getOptions()
+ .toBuilder()
+ .setBlobWriteSessionConfig(BlobWriteSessionConfigs.bufferToDiskThenUpload(path))
+ .build();
try (Storage s = options.getService()) {
doTest(s);
}
@@ -112,24 +96,13 @@ public void journalingNotSupportedByHttp() {
@Test
public void overrideDefaultBufferSize() throws Exception {
- StorageOptions options = null;
- if (transport == Transport.GRPC) {
- options =
- ((GrpcStorageOptions) storage.getOptions())
- .toBuilder()
- .setBlobWriteSessionConfig(
- BlobWriteSessionConfigs.getDefault().withChunkSize(256 * 1024))
- .build();
- } else if (transport == Transport.HTTP) {
- options =
- ((HttpStorageOptions) storage.getOptions())
- .toBuilder()
- .setBlobWriteSessionConfig(
- BlobWriteSessionConfigs.getDefault().withChunkSize(256 * 1024))
- .build();
- }
- assertWithMessage("unable to resolve options").that(options).isNotNull();
- //noinspection DataFlowIssue
+ StorageOptions options =
+ (storage.getOptions())
+ .toBuilder()
+ .setBlobWriteSessionConfig(
+ BlobWriteSessionConfigs.getDefault().withChunkSize(256 * 1024))
+ .build();
+
try (Storage s = options.getService()) {
doTest(s);
}
@@ -138,47 +111,16 @@ public void overrideDefaultBufferSize() throws Exception {
@Test
@CrossRun.Exclude(transports = Transport.HTTP)
public void bidiTest() throws Exception {
- StorageOptions options = null;
- if (transport == Transport.GRPC) {
- options =
- ((GrpcStorageOptions) storage.getOptions())
- .toBuilder()
- .setBlobWriteSessionConfig(BlobWriteSessionConfigs.bidiWrite())
- .build();
- }
- assertWithMessage("unable to resolve options").that(options).isNotNull();
-
+ StorageOptions options =
+ (storage.getOptions())
+ .toBuilder()
+ .setBlobWriteSessionConfig(BlobWriteSessionConfigs.bidiWrite())
+ .build();
try (Storage s = options.getService()) {
doTest(s);
}
}
- @Test
- public void closingAnOpenedSessionWithoutCallingWriteShouldMakeAnEmptyObject()
- throws IOException, ExecutionException, InterruptedException, TimeoutException {
- BlobInfo info = BlobInfo.newBuilder(bucket, generator.randomObjectName()).build();
- BlobWriteSession session = storage.blobWriteSession(info, BlobWriteOption.doesNotExist());
-
- WritableByteChannel open = session.open();
- open.close();
- BlobInfo gen1 = session.getResult().get(1, TimeUnit.SECONDS);
-
- assertThat(gen1.getSize()).isEqualTo(0);
- }
-
- @Test
- public void attemptingToOpenASessionWhichResultsInFailureShouldThrowAStorageException() {
- // attempt to write to a bucket which we have not created
- String badBucketName = bucket.getName() + "x";
- BlobInfo info = BlobInfo.newBuilder(badBucketName, generator.randomObjectName()).build();
-
- BlobWriteSession session = storage.blobWriteSession(info, BlobWriteOption.doesNotExist());
- StorageException se = assertThrows(StorageException.class, () -> session.open().close());
-
- assertThat(se.getCode()).isEqualTo(404);
- assertThat(se).hasMessageThat().contains(badBucketName);
- }
-
private void doTest(Storage underTest) throws Exception {
BlobWriteSession sess =
underTest.blobWriteSession(
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITJournalingBlobWriteSessionConfigTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITJournalingBlobWriteSessionConfigTest.java
index 4526a09f69..70e369f307 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITJournalingBlobWriteSessionConfigTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITJournalingBlobWriteSessionConfigTest.java
@@ -24,7 +24,6 @@
import com.google.cloud.storage.BlobWriteSessionConfigs;
import com.google.cloud.storage.BucketInfo;
import com.google.cloud.storage.DataGenerator;
-import com.google.cloud.storage.GrpcStorageOptions;
import com.google.cloud.storage.JournalingBlobWriteSessionConfig;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.Storage.BlobWriteOption;
@@ -75,7 +74,9 @@ public void setUp() throws Exception {
JournalingBlobWriteSessionConfig journaling =
BlobWriteSessionConfigs.journaling(ImmutableList.of(tempDir));
journalingStorage =
- ((GrpcStorageOptions.Builder) this.storage.getOptions().toBuilder())
+ this.storage
+ .getOptions()
+ .toBuilder()
.setBlobWriteSessionConfig(journaling)
.build()
.getService();
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITParallelCompositeUploadBlobWriteSessionConfigTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITParallelCompositeUploadBlobWriteSessionConfigTest.java
index 3352a92adc..9b7d630f6b 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITParallelCompositeUploadBlobWriteSessionConfigTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITParallelCompositeUploadBlobWriteSessionConfigTest.java
@@ -18,7 +18,6 @@
import static com.google.cloud.storage.TestUtils.xxd;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import com.google.api.gax.paging.Page;
@@ -30,8 +29,6 @@
import com.google.cloud.storage.BlobWriteSessionConfigs;
import com.google.cloud.storage.BucketInfo;
import com.google.cloud.storage.DataGenerator;
-import com.google.cloud.storage.GrpcStorageOptions;
-import com.google.cloud.storage.HttpStorageOptions;
import com.google.cloud.storage.ParallelCompositeUploadBlobWriteSessionConfig;
import com.google.cloud.storage.ParallelCompositeUploadBlobWriteSessionConfig.BufferAllocationStrategy;
import com.google.cloud.storage.ParallelCompositeUploadBlobWriteSessionConfig.ExecutorSupplier;
@@ -119,22 +116,8 @@ public void setUp() throws Exception {
// let our fixtures take care of cleaning things
.withPartCleanupStrategy(PartCleanupStrategy.never());
- StorageOptions storageOptions = null;
- if (transport == Transport.GRPC) {
- storageOptions =
- ((GrpcStorageOptions) injectedStorage.getOptions())
- .toBuilder()
- .setBlobWriteSessionConfig(pcu)
- .build();
- } else if (transport == Transport.HTTP) {
- storageOptions =
- ((HttpStorageOptions) injectedStorage.getOptions())
- .toBuilder()
- .setBlobWriteSessionConfig(pcu)
- .build();
- }
- assertWithMessage("unable to resolve options").that(storageOptions).isNotNull();
- //noinspection DataFlowIssue
+ StorageOptions storageOptions =
+ injectedStorage.getOptions().toBuilder().setBlobWriteSessionConfig(pcu).build();
storage = storageOptions.getService();
rand = new Random();
}