io.grpc.CallOptions Maven / Gradle / Ivy
/*
* Copyright 2015 The gRPC Authors
*
* 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 io.grpc;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* The collection of runtime options for a new RPC call.
*
* A field that is not set is {@code null}.
*/
@Immutable
@CheckReturnValue
public final class CallOptions {
/**
* A blank {@code CallOptions} that all fields are not set.
*/
public static final CallOptions DEFAULT;
static {
Builder b = new Builder();
b.customOptions = new Object[0][2];
b.streamTracerFactories = Collections.emptyList();
DEFAULT = b.build();
}
@Nullable
private final Deadline deadline;
@Nullable
private final Executor executor;
@Nullable
private final String authority;
@Nullable
private final CallCredentials credentials;
@Nullable
private final String compressorName;
private final Object[][] customOptions;
private final List streamTracerFactories;
/**
* Opposite to fail fast.
*/
@Nullable
private final Boolean waitForReady;
@Nullable
private final Integer maxInboundMessageSize;
@Nullable
private final Integer maxOutboundMessageSize;
@Nullable
private final Integer onReadyThreshold;
private CallOptions(Builder builder) {
this.deadline = builder.deadline;
this.executor = builder.executor;
this.authority = builder.authority;
this.credentials = builder.credentials;
this.compressorName = builder.compressorName;
this.customOptions = builder.customOptions;
this.streamTracerFactories = builder.streamTracerFactories;
this.waitForReady = builder.waitForReady;
this.maxInboundMessageSize = builder.maxInboundMessageSize;
this.maxOutboundMessageSize = builder.maxOutboundMessageSize;
this.onReadyThreshold = builder.onReadyThreshold;
}
static class Builder {
Deadline deadline;
Executor executor;
String authority;
CallCredentials credentials;
String compressorName;
Object[][] customOptions;
// Unmodifiable list
List streamTracerFactories;
Boolean waitForReady;
Integer maxInboundMessageSize;
Integer maxOutboundMessageSize;
Integer onReadyThreshold;
private CallOptions build() {
return new CallOptions(this);
}
}
/**
* Override the HTTP/2 authority the channel claims to be connecting to. This is not
* generally safe. Overriding allows advanced users to re-use a single Channel for multiple
* services, even if those services are hosted on different domain names. That assumes the
* server is virtually hosting multiple domains and is guaranteed to continue doing so. It is
* rare for a service provider to make such a guarantee. At this time, there is no security
* verification of the overridden value, such as making sure the authority matches the server's
* TLS certificate.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767")
public CallOptions withAuthority(@Nullable String authority) {
Builder builder = toBuilder(this);
builder.authority = authority;
return builder.build();
}
/**
* Returns a new {@code CallOptions} with the given call credentials.
*/
public CallOptions withCallCredentials(@Nullable CallCredentials credentials) {
Builder builder = toBuilder(this);
builder.credentials = credentials;
return builder.build();
}
/**
* Sets the compression to use for the call. The compressor must be a valid name known in the
* {@link CompressorRegistry}. By default, the "gzip" compressor will be available.
*
* It is only safe to call this if the server supports the compression format chosen. There is
* no negotiation performed; if the server does not support the compression chosen, the call will
* fail.
*/
public CallOptions withCompression(@Nullable String compressorName) {
Builder builder = toBuilder(this);
builder.compressorName = compressorName;
return builder.build();
}
/**
* Returns a new {@code CallOptions} with the given absolute deadline.
*
*
This is mostly used for propagating an existing deadline. {@link #withDeadlineAfter} is the
* recommended way of setting a new deadline,
*
* @param deadline the deadline or {@code null} for unsetting the deadline.
*/
public CallOptions withDeadline(@Nullable Deadline deadline) {
Builder builder = toBuilder(this);
builder.deadline = deadline;
return builder.build();
}
/**
* Returns a new {@code CallOptions} with a deadline that is after the given {@code duration} from
* now.
*/
public CallOptions withDeadlineAfter(long duration, TimeUnit unit) {
return withDeadline(Deadline.after(duration, unit));
}
/**
* Returns the deadline or {@code null} if the deadline is not set.
*/
@Nullable
public Deadline getDeadline() {
return deadline;
}
/**
* Enables
* 'wait for ready' for the call. Wait-for-ready queues the RPC until a connection is
* available. This may dramatically increase the latency of the RPC, but avoids failing
* "unnecessarily." The default queues the RPC until an attempt to connect has completed, but
* fails RPCs without sending them if unable to connect.
*/
public CallOptions withWaitForReady() {
Builder builder = toBuilder(this);
builder.waitForReady = Boolean.TRUE;
return builder.build();
}
/**
* Disables 'wait for ready' feature for the call.
* This method should be rarely used because the default is without 'wait for ready'.
*/
public CallOptions withoutWaitForReady() {
Builder builder = toBuilder(this);
builder.waitForReady = Boolean.FALSE;
return builder.build();
}
/**
* Specifies how many bytes must be queued before the call is
* considered not ready to send more messages.
*
* @param numBytes The number of bytes that must be queued. Must be a
* positive integer.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/11021")
public CallOptions withOnReadyThreshold(int numBytes) {
checkArgument(numBytes > 0, "numBytes must be positive: %s", numBytes);
Builder builder = toBuilder(this);
builder.onReadyThreshold = numBytes;
return builder.build();
}
/**
* Resets to the default number of bytes that must be queued before the
* call will leave the
* 'wait for ready' state.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/11021")
public CallOptions clearOnReadyThreshold() {
Builder builder = toBuilder(this);
builder.onReadyThreshold = null;
return builder.build();
}
/**
* Returns to the default number of bytes that must be queued before the
* call will leave the
* 'wait for ready' state.
*
* @return null if the default threshold is used.
*/
@Nullable
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/11021")
public Integer getOnReadyThreshold() {
return onReadyThreshold;
}
/**
* Returns the compressor's name.
*/
@Nullable
public String getCompressor() {
return compressorName;
}
/**
* Override the HTTP/2 authority the channel claims to be connecting to. This is not
* generally safe. Overriding allows advanced users to re-use a single Channel for multiple
* services, even if those services are hosted on different domain names. That assumes the
* server is virtually hosting multiple domains and is guaranteed to continue doing so. It is
* rare for a service provider to make such a guarantee. At this time, there is no security
* verification of the overridden value, such as making sure the authority matches the server's
* TLS certificate.
*/
@Nullable
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767")
public String getAuthority() {
return authority;
}
/**
* Returns the call credentials.
*/
@Nullable
public CallCredentials getCredentials() {
return credentials;
}
/**
* Returns a new {@code CallOptions} with {@code executor} to be used instead of the default
* executor specified with {@link ManagedChannelBuilder#executor}.
*/
public CallOptions withExecutor(@Nullable Executor executor) {
Builder builder = toBuilder(this);
builder.executor = executor;
return builder.build();
}
/**
* Returns a new {@code CallOptions} with a {@code ClientStreamTracerFactory} in addition to
* the existing factories.
*
*
This method doesn't replace existing factories, or try to de-duplicate factories.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861")
public CallOptions withStreamTracerFactory(ClientStreamTracer.Factory factory) {
ArrayList newList =
new ArrayList<>(streamTracerFactories.size() + 1);
newList.addAll(streamTracerFactories);
newList.add(factory);
Builder builder = toBuilder(this);
builder.streamTracerFactories = Collections.unmodifiableList(newList);
return builder.build();
}
/**
* Returns an immutable list of {@code ClientStreamTracerFactory}s.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861")
public List getStreamTracerFactories() {
return streamTracerFactories;
}
/**
* Key for a key-value pair. Uses reference equality.
*/
public static final class Key {
private final String debugString;
private final T defaultValue;
private Key(String debugString, T defaultValue) {
this.debugString = debugString;
this.defaultValue = defaultValue;
}
/**
* Returns the user supplied default value for this key.
*/
public T getDefault() {
return defaultValue;
}
@Override
public String toString() {
return debugString;
}
/**
* Factory method for creating instances of {@link Key}.
*
* @param debugString a string used to describe this key, used for debugging.
* @param defaultValue default value to return when value for key not set
* @param Key type
* @return Key object
* @deprecated Use {@link #create} or {@link #createWithDefault} instead. This method will
* be removed.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869")
@Deprecated
public static Key of(String debugString, T defaultValue) {
Preconditions.checkNotNull(debugString, "debugString");
return new Key<>(debugString, defaultValue);
}
/**
* Factory method for creating instances of {@link Key}. The default value of the
* key is {@code null}.
*
* @param debugString a debug string that describes this key.
* @param Key type
* @return Key object
* @since 1.13.0
*/
public static Key create(String debugString) {
Preconditions.checkNotNull(debugString, "debugString");
return new Key<>(debugString, /*defaultValue=*/ null);
}
/**
* Factory method for creating instances of {@link Key}.
*
* @param debugString a debug string that describes this key.
* @param defaultValue default value to return when value for key not set
* @param Key type
* @return Key object
* @since 1.13.0
*/
public static Key createWithDefault(String debugString, T defaultValue) {
Preconditions.checkNotNull(debugString, "debugString");
return new Key<>(debugString, defaultValue);
}
}
/**
* Sets a custom option. Any existing value for the key is overwritten.
*
* @param key The option key
* @param value The option value.
* @since 1.13.0
*/
public CallOptions withOption(Key key, T value) {
Preconditions.checkNotNull(key, "key");
Preconditions.checkNotNull(value, "value");
Builder builder = toBuilder(this);
int existingIdx = -1;
for (int i = 0; i < customOptions.length; i++) {
if (key.equals(customOptions[i][0])) {
existingIdx = i;
break;
}
}
builder.customOptions = new Object[customOptions.length + (existingIdx == -1 ? 1 : 0)][2];
System.arraycopy(customOptions, 0, builder.customOptions, 0, customOptions.length);
if (existingIdx == -1) {
// Add a new option
builder.customOptions[customOptions.length] = new Object[] {key, value};
} else {
// Replace an existing option
builder.customOptions[existingIdx] = new Object[] {key, value};
}
return builder.build();
}
/**
* Get the value for a custom option or its inherent default.
* @param key Key identifying option
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869")
@SuppressWarnings("unchecked")
public T getOption(Key key) {
Preconditions.checkNotNull(key, "key");
for (int i = 0; i < customOptions.length; i++) {
if (key.equals(customOptions[i][0])) {
return (T) customOptions[i][1];
}
}
return key.defaultValue;
}
/**
* Returns the executor override to use for this specific call, or {@code null} if there is no
* override. The executor is only for servicing this one call, so is not safe to use after
* {@link ClientCall.Listener#onClose}.
*/
@Nullable
public Executor getExecutor() {
return executor;
}
/**
* Returns whether
* 'wait for ready' option is enabled for the call. 'Fail fast' is the default option for gRPC
* calls and 'wait for ready' is the opposite to it.
*/
public boolean isWaitForReady() {
return Boolean.TRUE.equals(waitForReady);
}
Boolean getWaitForReady() {
return waitForReady;
}
/**
* Sets the maximum allowed message size acceptable from the remote peer. If unset, this will
* default to the value set on the {@link ManagedChannelBuilder#maxInboundMessageSize(int)}.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
public CallOptions withMaxInboundMessageSize(int maxSize) {
checkArgument(maxSize >= 0, "invalid maxsize %s", maxSize);
Builder builder = toBuilder(this);
builder.maxInboundMessageSize = maxSize;
return builder.build();
}
/**
* Sets the maximum allowed message size acceptable sent to the remote peer.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
public CallOptions withMaxOutboundMessageSize(int maxSize) {
checkArgument(maxSize >= 0, "invalid maxsize %s", maxSize);
Builder builder = toBuilder(this);
builder.maxOutboundMessageSize = maxSize;
return builder.build();
}
/**
* Gets the maximum allowed message size acceptable from the remote peer.
*/
@Nullable
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
public Integer getMaxInboundMessageSize() {
return maxInboundMessageSize;
}
/**
* Gets the maximum allowed message size acceptable to send the remote peer.
*/
@Nullable
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
public Integer getMaxOutboundMessageSize() {
return maxOutboundMessageSize;
}
/**
* Copy CallOptions.
*/
private static Builder toBuilder(CallOptions other) {
Builder builder = new Builder();
builder.deadline = other.deadline;
builder.executor = other.executor;
builder.authority = other.authority;
builder.credentials = other.credentials;
builder.compressorName = other.compressorName;
builder.customOptions = other.customOptions;
builder.streamTracerFactories = other.streamTracerFactories;
builder.waitForReady = other.waitForReady;
builder.maxInboundMessageSize = other.maxInboundMessageSize;
builder.maxOutboundMessageSize = other.maxOutboundMessageSize;
return builder;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("deadline", deadline)
.add("authority", authority)
.add("callCredentials", credentials)
.add("executor", executor != null ? executor.getClass() : null)
.add("compressorName", compressorName)
.add("customOptions", Arrays.deepToString(customOptions))
.add("waitForReady", isWaitForReady())
.add("maxInboundMessageSize", maxInboundMessageSize)
.add("maxOutboundMessageSize", maxOutboundMessageSize)
.add("streamTracerFactories", streamTracerFactories)
.toString();
}
}