com.google.cloud.storage.HttpWritableByteChannelSessionBuilder Maven / Gradle / Ivy
Show all versions of google-cloud-storage Show documentation
/*
* Copyright 2023 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;
import static java.util.Objects.requireNonNull;
import com.google.api.core.ApiFuture;
import com.google.api.core.SettableApiFuture;
import com.google.api.gax.retrying.ResultRetryAlgorithm;
import com.google.api.services.storage.model.StorageObject;
import com.google.cloud.storage.ChannelSession.BufferedWriteSession;
import com.google.cloud.storage.ChannelSession.UnbufferedWriteSession;
import com.google.cloud.storage.Retrying.RetryingDependencies;
import com.google.cloud.storage.UnbufferedWritableByteChannelSession.UnbufferedWritableByteChannel;
import java.nio.ByteBuffer;
import java.util.function.BiFunction;
import java.util.function.LongConsumer;
import org.checkerframework.checker.nullness.qual.NonNull;
final class HttpWritableByteChannelSessionBuilder {
private static final int DEFAULT_BUFFER_CAPACITY = ByteSizeConstants._16MiB;
@NonNull private final HttpClientContext httpClientContext;
HttpWritableByteChannelSessionBuilder(@NonNull HttpClientContext httpClientContext) {
this.httpClientContext =
requireNonNull(httpClientContext, "httpClientContext must be non null");
}
/**
* The build {@link WritableByteChannelSession} will perform a "Resumable" upload.
*
* A "Resumable" upload will sync the transmitted data with GCS upon each individual flush and
* when the channel is closed.
*
*
If an error is returned the individual flush can be transparently retried.
*/
ResumableUploadBuilder resumable() {
return new ResumableUploadBuilder(httpClientContext);
}
static final class ResumableUploadBuilder {
@NonNull private final HttpClientContext httpClientContext;
private RetryingDependencies deps;
private ResultRetryAlgorithm> alg;
private LongConsumer committedBytesCallback;
ResumableUploadBuilder(@NonNull HttpClientContext httpClientContext) {
this.httpClientContext = httpClientContext;
this.deps = RetryingDependencies.attemptOnce();
this.alg = Retrying.neverRetry();
this.committedBytesCallback = l -> {};
}
ResumableUploadBuilder setCommittedBytesCallback(@NonNull LongConsumer committedBytesCallback) {
this.committedBytesCallback =
requireNonNull(committedBytesCallback, "committedBytesCallback must be non null");
return this;
}
ResumableUploadBuilder withRetryConfig(
@NonNull RetryingDependencies deps, @NonNull ResultRetryAlgorithm> alg) {
this.deps = requireNonNull(deps, "deps must be non null");
this.alg = requireNonNull(alg, "alg must be non null");
return this;
}
/**
* Do not apply any intermediate buffering. Any call to {@link
* java.nio.channels.WritableByteChannel#write(ByteBuffer)} will be segmented as is and sent to
* GCS.
*
*
Note: this is considered an advanced API, and should not be used in circumstances in which
* control of {@link ByteBuffer}s sent to {@code write} is not self-contained.
*/
UnbufferedResumableUploadBuilder unbuffered() {
return new UnbufferedResumableUploadBuilder();
}
/** Buffer up to {@link #DEFAULT_BUFFER_CAPACITY} worth of bytes before attempting to flush */
BufferedResumableUploadBuilder buffered() {
return buffered(BufferHandle.allocate(DEFAULT_BUFFER_CAPACITY));
}
/**
* Buffer using {@code byteBuffer} worth of space before attempting to flush.
*
*
The provided {@link ByteBuffer} should be aligned with GCSs block size of 256
* KiB.
*/
BufferedResumableUploadBuilder buffered(ByteBuffer byteBuffer) {
return buffered(BufferHandle.handleOf(byteBuffer));
}
BufferedResumableUploadBuilder buffered(BufferHandle bufferHandle) {
return new BufferedResumableUploadBuilder(bufferHandle);
}
/**
* When constructing any of our channel sessions, there is always a {@link
* GapicUnbufferedWritableByteChannel} at the bottom of it. This method creates a BiFunction
* which will instantiate the {@link GapicUnbufferedWritableByteChannel} when provided with a
* {@code StartT} value and a {@code SettableApiFuture}.
*
* As part of providing the function, the provided parameters {@code FlusherFactory} and
* {@code f} are "bound" into the returned function. In conjunction with the configured fields
* of this class a new instance of {@link GapicUnbufferedWritableByteChannel} can be
* constructed.
*/
private BiFunction<
JsonResumableWrite, SettableApiFuture, UnbufferedWritableByteChannel>
bindFunction() {
// it is theoretically possible that the setter methods for the following variables could
// be called again between when this method is invoked and the resulting function is invoked.
// To ensure we are using the specified values at the point in time they are bound to the
// function read them into local variables which will be closed over rather than the class
// fields.
RetryingDependencies boundDeps = deps;
ResultRetryAlgorithm> boundAlg = alg;
return (start, resultFuture) ->
new ApiaryUnbufferedWritableByteChannel(
httpClientContext, boundDeps, boundAlg, start, resultFuture, committedBytesCallback);
}
final class UnbufferedResumableUploadBuilder {
private ApiFuture start;
/**
* Set the Future which will contain the ResumableWrite information necessary to open the
* Write stream.
*/
UnbufferedResumableUploadBuilder setStartAsync(ApiFuture start) {
this.start = requireNonNull(start, "start must be non null");
return this;
}
UnbufferedWritableByteChannelSession build() {
return new UnbufferedWriteSession<>(
requireNonNull(start, "start must be non null"),
bindFunction().andThen(StorageByteChannels.writable()::createSynchronized));
}
}
final class BufferedResumableUploadBuilder {
private final BufferHandle bufferHandle;
private ApiFuture start;
BufferedResumableUploadBuilder(BufferHandle bufferHandle) {
this.bufferHandle = bufferHandle;
}
/**
* Set the Future which will contain the ResumableWrite information necessary to open the
* Write stream.
*/
BufferedResumableUploadBuilder setStartAsync(ApiFuture start) {
this.start = requireNonNull(start, "start must be non null");
return this;
}
BufferedWritableByteChannelSession build() {
return new BufferedWriteSession<>(
requireNonNull(start, "start must be non null"),
bindFunction()
.andThen(c -> new DefaultBufferedWritableByteChannel(bufferHandle, c))
.andThen(StorageByteChannels.writable()::createSynchronized));
}
}
}
}