| /* |
| * Copyright 2015, Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| 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.Arrays; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.annotation.Nullable; |
| import javax.annotation.concurrent.Immutable; |
| |
| /** |
| * The collection of runtime options for a new RPC call. |
| * |
| * <p>A field that is not set is {@code null}. |
| */ |
| @Immutable |
| public final class CallOptions { |
| /** |
| * A blank {@code CallOptions} that all fields are not set. |
| */ |
| public static final CallOptions DEFAULT = new CallOptions(); |
| |
| // Although {@code CallOptions} is immutable, its fields are not final, so that we can initialize |
| // them outside of constructor. Otherwise the constructor will have a potentially long list of |
| // unnamed arguments, which is undesirable. |
| private Deadline deadline; |
| private Executor executor; |
| |
| @Nullable |
| private String authority; |
| |
| @Nullable |
| private CallCredentials credentials; |
| |
| private Attributes affinity = Attributes.EMPTY; |
| |
| @Nullable |
| private String compressorName; |
| |
| private Object[][] customOptions = new Object[0][2]; |
| |
| /** |
| * Opposite to fail fast. |
| */ |
| private boolean waitForReady; |
| |
| @Nullable |
| private Integer maxInboundMessageSize; |
| @Nullable |
| private Integer maxOutboundMessageSize; |
| |
| |
| /** |
| * Override the HTTP/2 authority the channel claims to be connecting to. <em>This is not |
| * generally safe.</em> 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. <em>At this time, there is no security |
| * verification of the overridden value, such as making sure the authority matches the server's |
| * TLS certificate.</em> |
| */ |
| @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767") |
| public CallOptions withAuthority(@Nullable String authority) { |
| CallOptions newOptions = new CallOptions(this); |
| newOptions.authority = authority; |
| return newOptions; |
| } |
| |
| /** |
| * Returns a new {@code CallOptions} with the given call credentials. |
| */ |
| @ExperimentalApi("https//github.com/grpc/grpc-java/issues/1914") |
| public CallOptions withCallCredentials(@Nullable CallCredentials credentials) { |
| CallOptions newOptions = new CallOptions(this); |
| newOptions.credentials = credentials; |
| return newOptions; |
| } |
| |
| /** |
| * Sets the compression to use for the call. The compressor must be a valid name known in the |
| * {@link CompressorRegistry}. |
| */ |
| @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1704") |
| public CallOptions withCompression(@Nullable String compressorName) { |
| CallOptions newOptions = new CallOptions(this); |
| newOptions.compressorName = compressorName; |
| return newOptions; |
| } |
| |
| /** |
| * Returns a new {@code CallOptions} with the given absolute deadline. |
| * |
| * <p>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) { |
| CallOptions newOptions = new CallOptions(this); |
| newOptions.deadline = deadline; |
| return newOptions; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Returns a new {@code CallOptions} with attributes for affinity-based routing. |
| */ |
| @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1766") |
| public CallOptions withAffinity(Attributes affinity) { |
| CallOptions newOptions = new CallOptions(this); |
| newOptions.affinity = Preconditions.checkNotNull(affinity, "affinity"); |
| return newOptions; |
| } |
| |
| /** |
| * Enables 'wait for ready' feature for the call. |
| * <a href="https://github.com/grpc/grpc/blob/master/doc/fail_fast.md">'Fail fast'</a> |
| * is the default option for gRPC calls and 'wait for ready' is the opposite to it. |
| */ |
| public CallOptions withWaitForReady() { |
| CallOptions newOptions = new CallOptions(this); |
| newOptions.waitForReady = true; |
| return newOptions; |
| } |
| |
| /** |
| * 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() { |
| CallOptions newOptions = new CallOptions(this); |
| newOptions.waitForReady = false; |
| return newOptions; |
| } |
| |
| /** |
| * Returns the attributes for affinity-based routing. |
| */ |
| @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1766") |
| public Attributes getAffinity() { |
| return affinity; |
| } |
| |
| /** |
| * Returns the compressor's name. |
| */ |
| @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1704") |
| @Nullable |
| public String getCompressor() { |
| return compressorName; |
| } |
| |
| /** |
| * Override the HTTP/2 authority the channel claims to be connecting to. <em>This is not |
| * generally safe.</em> 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. <em>At this time, there is no security |
| * verification of the overridden value, such as making sure the authority matches the server's |
| * TLS certificate.</em> |
| */ |
| @Nullable |
| @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767") |
| public String getAuthority() { |
| return authority; |
| } |
| |
| /** |
| * Returns the call credentials. |
| */ |
| @ExperimentalApi("https//github.com/grpc/grpc-java/issues/1914") |
| @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(Executor executor) { |
| CallOptions newOptions = new CallOptions(this); |
| newOptions.executor = executor; |
| return newOptions; |
| } |
| |
| @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869") |
| public static final class Key<T> { |
| private final String name; |
| private final T defaultValue; |
| |
| private Key(String name, T defaultValue) { |
| this.name = name; |
| this.defaultValue = defaultValue; |
| } |
| |
| public T getDefault() { |
| return defaultValue; |
| } |
| |
| @Override |
| public String toString() { |
| return name; |
| } |
| |
| /** |
| * Factory method for creating instances of {@link Key}. |
| * |
| * @param name the name of Key. |
| * @param defaultValue default value to return when value for key not set |
| * @param <T> Key type |
| * @return Key object |
| */ |
| public static <T> Key<T> of(String name, T defaultValue) { |
| Preconditions.checkNotNull(name, "name"); |
| return new Key<T>(name, defaultValue); |
| } |
| } |
| |
| /** |
| * Sets a custom option. Any existing value for the key is overwritten. |
| * |
| * @param key The option key |
| * @param value The option value. |
| */ |
| @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869") |
| public <T> CallOptions withOption(Key<T> key, T value) { |
| Preconditions.checkNotNull(key, "key"); |
| Preconditions.checkNotNull(value, "value"); |
| |
| CallOptions newOptions = new CallOptions(this); |
| int existingIdx = -1; |
| for (int i = 0; i < customOptions.length; i++) { |
| if (key.equals(customOptions[i][0])) { |
| existingIdx = i; |
| break; |
| } |
| } |
| |
| newOptions.customOptions = new Object[customOptions.length + (existingIdx == -1 ? 1 : 0)][2]; |
| System.arraycopy(customOptions, 0, newOptions.customOptions, 0, customOptions.length); |
| |
| if (existingIdx == -1) { |
| // Add a new option |
| newOptions.customOptions[customOptions.length] = new Object[] {key, value}; |
| } else { |
| // Replace an existing option |
| newOptions.customOptions[existingIdx][1] = value; |
| } |
| |
| return newOptions; |
| } |
| |
| /** |
| * 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> T getOption(Key<T> 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; |
| } |
| |
| @Nullable |
| public Executor getExecutor() { |
| return executor; |
| } |
| |
| private CallOptions() { |
| } |
| |
| /** |
| * Returns whether 'wait for ready' option is enabled for the call. |
| * <a href="https://github.com/grpc/grpc/blob/master/doc/fail_fast.md">'Fail fast'</a> |
| * is the default option for gRPC calls and 'wait for ready' is the opposite to it. |
| */ |
| public boolean isWaitForReady() { |
| 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); |
| CallOptions newOptions = new CallOptions(this); |
| newOptions.maxInboundMessageSize = maxSize; |
| return newOptions; |
| } |
| |
| /** |
| * 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); |
| CallOptions newOptions = new CallOptions(this); |
| newOptions.maxOutboundMessageSize = maxSize; |
| return newOptions; |
| } |
| |
| /** |
| * 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 constructor. |
| */ |
| private CallOptions(CallOptions other) { |
| deadline = other.deadline; |
| authority = other.authority; |
| credentials = other.credentials; |
| affinity = other.affinity; |
| executor = other.executor; |
| compressorName = other.compressorName; |
| customOptions = other.customOptions; |
| waitForReady = other.waitForReady; |
| maxInboundMessageSize = other.maxInboundMessageSize; |
| maxOutboundMessageSize = other.maxOutboundMessageSize; |
| } |
| |
| @Override |
| public String toString() { |
| return MoreObjects.toStringHelper(this) |
| .add("deadline", deadline) |
| .add("authority", authority) |
| .add("callCredentials", credentials) |
| .add("affinity", affinity) |
| .add("executor", executor != null ? executor.getClass() : null) |
| .add("compressorName", compressorName) |
| .add("customOptions", Arrays.deepToString(customOptions)) |
| .add("waitForReady", isWaitForReady()) |
| .add("maxInboundMessageSize", maxInboundMessageSize) |
| .add("maxOutboundMessageSize", maxOutboundMessageSize) |
| .toString(); |
| } |
| } |