1 /* 2 * Copyright 2015 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 21 import com.google.common.base.MoreObjects; 22 import com.google.common.base.Preconditions; 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.Collections; 26 import java.util.List; 27 import java.util.concurrent.Executor; 28 import java.util.concurrent.TimeUnit; 29 import javax.annotation.CheckReturnValue; 30 import javax.annotation.Nullable; 31 import javax.annotation.concurrent.Immutable; 32 33 /** 34 * The collection of runtime options for a new RPC call. 35 * 36 * <p>A field that is not set is {@code null}. 37 */ 38 @Immutable 39 @CheckReturnValue 40 public final class CallOptions { 41 /** 42 * A blank {@code CallOptions} that all fields are not set. 43 */ 44 public static final CallOptions DEFAULT = new CallOptions(); 45 46 // Although {@code CallOptions} is immutable, its fields are not final, so that we can initialize 47 // them outside of constructor. Otherwise the constructor will have a potentially long list of 48 // unnamed arguments, which is undesirable. 49 private Deadline deadline; 50 private Executor executor; 51 52 @Nullable 53 private String authority; 54 55 @Nullable 56 private CallCredentials credentials; 57 58 @Nullable 59 private String compressorName; 60 61 private Object[][] customOptions = new Object[0][2]; 62 63 // Unmodifiable list 64 private List<ClientStreamTracer.Factory> streamTracerFactories = Collections.emptyList(); 65 66 /** 67 * Opposite to fail fast. 68 */ 69 private boolean waitForReady; 70 71 @Nullable 72 private Integer maxInboundMessageSize; 73 @Nullable 74 private Integer maxOutboundMessageSize; 75 76 77 /** 78 * Override the HTTP/2 authority the channel claims to be connecting to. <em>This is not 79 * generally safe.</em> Overriding allows advanced users to re-use a single Channel for multiple 80 * services, even if those services are hosted on different domain names. That assumes the 81 * server is virtually hosting multiple domains and is guaranteed to continue doing so. It is 82 * rare for a service provider to make such a guarantee. <em>At this time, there is no security 83 * verification of the overridden value, such as making sure the authority matches the server's 84 * TLS certificate.</em> 85 */ 86 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767") withAuthority(@ullable String authority)87 public CallOptions withAuthority(@Nullable String authority) { 88 CallOptions newOptions = new CallOptions(this); 89 newOptions.authority = authority; 90 return newOptions; 91 } 92 93 /** 94 * Returns a new {@code CallOptions} with the given call credentials. 95 */ withCallCredentials(@ullable CallCredentials credentials)96 public CallOptions withCallCredentials(@Nullable CallCredentials credentials) { 97 CallOptions newOptions = new CallOptions(this); 98 newOptions.credentials = credentials; 99 return newOptions; 100 } 101 102 /** 103 * Sets the compression to use for the call. The compressor must be a valid name known in the 104 * {@link CompressorRegistry}. 105 */ 106 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1704") withCompression(@ullable String compressorName)107 public CallOptions withCompression(@Nullable String compressorName) { 108 CallOptions newOptions = new CallOptions(this); 109 newOptions.compressorName = compressorName; 110 return newOptions; 111 } 112 113 /** 114 * Returns a new {@code CallOptions} with the given absolute deadline. 115 * 116 * <p>This is mostly used for propagating an existing deadline. {@link #withDeadlineAfter} is the 117 * recommended way of setting a new deadline, 118 * 119 * @param deadline the deadline or {@code null} for unsetting the deadline. 120 */ withDeadline(@ullable Deadline deadline)121 public CallOptions withDeadline(@Nullable Deadline deadline) { 122 CallOptions newOptions = new CallOptions(this); 123 newOptions.deadline = deadline; 124 return newOptions; 125 } 126 127 /** 128 * Returns a new {@code CallOptions} with a deadline that is after the given {@code duration} from 129 * now. 130 */ withDeadlineAfter(long duration, TimeUnit unit)131 public CallOptions withDeadlineAfter(long duration, TimeUnit unit) { 132 return withDeadline(Deadline.after(duration, unit)); 133 } 134 135 /** 136 * Returns the deadline or {@code null} if the deadline is not set. 137 */ 138 @Nullable getDeadline()139 public Deadline getDeadline() { 140 return deadline; 141 } 142 143 /** 144 * Enables <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md"> 145 * 'wait for ready'</a> feature for the call. 'Fail fast' is the default option for gRPC calls 146 * and 'wait for ready' is the opposite to it. 147 */ withWaitForReady()148 public CallOptions withWaitForReady() { 149 CallOptions newOptions = new CallOptions(this); 150 newOptions.waitForReady = true; 151 return newOptions; 152 } 153 154 /** 155 * Disables 'wait for ready' feature for the call. 156 * This method should be rarely used because the default is without 'wait for ready'. 157 */ withoutWaitForReady()158 public CallOptions withoutWaitForReady() { 159 CallOptions newOptions = new CallOptions(this); 160 newOptions.waitForReady = false; 161 return newOptions; 162 } 163 164 /** 165 * Returns the compressor's name. 166 */ 167 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1704") 168 @Nullable getCompressor()169 public String getCompressor() { 170 return compressorName; 171 } 172 173 /** 174 * Override the HTTP/2 authority the channel claims to be connecting to. <em>This is not 175 * generally safe.</em> Overriding allows advanced users to re-use a single Channel for multiple 176 * services, even if those services are hosted on different domain names. That assumes the 177 * server is virtually hosting multiple domains and is guaranteed to continue doing so. It is 178 * rare for a service provider to make such a guarantee. <em>At this time, there is no security 179 * verification of the overridden value, such as making sure the authority matches the server's 180 * TLS certificate.</em> 181 */ 182 @Nullable 183 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767") getAuthority()184 public String getAuthority() { 185 return authority; 186 } 187 188 /** 189 * Returns the call credentials. 190 */ 191 @Nullable getCredentials()192 public CallCredentials getCredentials() { 193 return credentials; 194 } 195 196 /** 197 * Returns a new {@code CallOptions} with {@code executor} to be used instead of the default 198 * executor specified with {@link ManagedChannelBuilder#executor}. 199 */ withExecutor(Executor executor)200 public CallOptions withExecutor(Executor executor) { 201 CallOptions newOptions = new CallOptions(this); 202 newOptions.executor = executor; 203 return newOptions; 204 } 205 206 /** 207 * Returns a new {@code CallOptions} with a {@code ClientStreamTracerFactory} in addition to 208 * the existing factories. 209 * 210 * <p>This method doesn't replace existing factories, or try to de-duplicate factories. 211 */ 212 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861") withStreamTracerFactory(ClientStreamTracer.Factory factory)213 public CallOptions withStreamTracerFactory(ClientStreamTracer.Factory factory) { 214 CallOptions newOptions = new CallOptions(this); 215 ArrayList<ClientStreamTracer.Factory> newList = 216 new ArrayList<ClientStreamTracer.Factory>(streamTracerFactories.size() + 1); 217 newList.addAll(streamTracerFactories); 218 newList.add(factory); 219 newOptions.streamTracerFactories = Collections.unmodifiableList(newList); 220 return newOptions; 221 } 222 223 /** 224 * Returns an immutable list of {@code ClientStreamTracerFactory}s. 225 */ 226 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861") getStreamTracerFactories()227 public List<ClientStreamTracer.Factory> getStreamTracerFactories() { 228 return streamTracerFactories; 229 } 230 231 /** 232 * Key for a key-value pair. Uses reference equality. 233 */ 234 public static final class Key<T> { 235 private final String debugString; 236 private final T defaultValue; 237 Key(String debugString, T defaultValue)238 private Key(String debugString, T defaultValue) { 239 this.debugString = debugString; 240 this.defaultValue = defaultValue; 241 } 242 243 /** 244 * Returns the user supplied default value for this key. 245 */ getDefault()246 public T getDefault() { 247 return defaultValue; 248 } 249 250 @Override toString()251 public String toString() { 252 return debugString; 253 } 254 255 /** 256 * Factory method for creating instances of {@link Key}. 257 * 258 * @param debugString a string used to describe this key, used for debugging. 259 * @param defaultValue default value to return when value for key not set 260 * @param <T> Key type 261 * @return Key object 262 * @deprecated Use {@link #create} or {@link #createWithDefault} instead. This method will 263 * be removed. 264 */ 265 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869") 266 @Deprecated of(String debugString, T defaultValue)267 public static <T> Key<T> of(String debugString, T defaultValue) { 268 Preconditions.checkNotNull(debugString, "debugString"); 269 return new Key<T>(debugString, defaultValue); 270 } 271 272 /** 273 * Factory method for creating instances of {@link Key}. The default value of the 274 * key is {@code null}. 275 * 276 * @param debugString a debug string that describes this key. 277 * @param <T> Key type 278 * @return Key object 279 * @since 1.13.0 280 */ create(String debugString)281 public static <T> Key<T> create(String debugString) { 282 Preconditions.checkNotNull(debugString, "debugString"); 283 return new Key<T>(debugString, /*defaultValue=*/ null); 284 } 285 286 /** 287 * Factory method for creating instances of {@link Key}. 288 * 289 * @param debugString a debug string that describes this key. 290 * @param defaultValue default value to return when value for key not set 291 * @param <T> Key type 292 * @return Key object 293 * @since 1.13.0 294 */ createWithDefault(String debugString, T defaultValue)295 public static <T> Key<T> createWithDefault(String debugString, T defaultValue) { 296 Preconditions.checkNotNull(debugString, "debugString"); 297 return new Key<T>(debugString, defaultValue); 298 } 299 } 300 301 /** 302 * Sets a custom option. Any existing value for the key is overwritten. 303 * 304 * @param key The option key 305 * @param value The option value. 306 * @since 1.13.0 307 */ withOption(Key<T> key, T value)308 public <T> CallOptions withOption(Key<T> key, T value) { 309 Preconditions.checkNotNull(key, "key"); 310 Preconditions.checkNotNull(value, "value"); 311 312 CallOptions newOptions = new CallOptions(this); 313 int existingIdx = -1; 314 for (int i = 0; i < customOptions.length; i++) { 315 if (key.equals(customOptions[i][0])) { 316 existingIdx = i; 317 break; 318 } 319 } 320 321 newOptions.customOptions = new Object[customOptions.length + (existingIdx == -1 ? 1 : 0)][2]; 322 System.arraycopy(customOptions, 0, newOptions.customOptions, 0, customOptions.length); 323 324 if (existingIdx == -1) { 325 // Add a new option 326 newOptions.customOptions[customOptions.length] = new Object[] {key, value}; 327 } else { 328 // Replace an existing option 329 newOptions.customOptions[existingIdx][1] = value; 330 } 331 332 return newOptions; 333 } 334 335 /** 336 * Get the value for a custom option or its inherent default. 337 * @param key Key identifying option 338 */ 339 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869") 340 @SuppressWarnings("unchecked") getOption(Key<T> key)341 public <T> T getOption(Key<T> key) { 342 Preconditions.checkNotNull(key, "key"); 343 for (int i = 0; i < customOptions.length; i++) { 344 if (key.equals(customOptions[i][0])) { 345 return (T) customOptions[i][1]; 346 } 347 } 348 return key.defaultValue; 349 } 350 351 @Nullable getExecutor()352 public Executor getExecutor() { 353 return executor; 354 } 355 CallOptions()356 private CallOptions() { 357 } 358 359 /** 360 * Returns whether <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md"> 361 * 'wait for ready'</a> option is enabled for the call. 'Fail fast' is the default option for gRPC 362 * calls and 'wait for ready' is the opposite to it. 363 */ isWaitForReady()364 public boolean isWaitForReady() { 365 return waitForReady; 366 } 367 368 /** 369 * Sets the maximum allowed message size acceptable from the remote peer. If unset, this will 370 * default to the value set on the {@link ManagedChannelBuilder#maxInboundMessageSize(int)}. 371 */ 372 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563") withMaxInboundMessageSize(int maxSize)373 public CallOptions withMaxInboundMessageSize(int maxSize) { 374 checkArgument(maxSize >= 0, "invalid maxsize %s", maxSize); 375 CallOptions newOptions = new CallOptions(this); 376 newOptions.maxInboundMessageSize = maxSize; 377 return newOptions; 378 } 379 380 /** 381 * Sets the maximum allowed message size acceptable sent to the remote peer. 382 */ 383 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563") withMaxOutboundMessageSize(int maxSize)384 public CallOptions withMaxOutboundMessageSize(int maxSize) { 385 checkArgument(maxSize >= 0, "invalid maxsize %s", maxSize); 386 CallOptions newOptions = new CallOptions(this); 387 newOptions.maxOutboundMessageSize = maxSize; 388 return newOptions; 389 } 390 391 /** 392 * Gets the maximum allowed message size acceptable from the remote peer. 393 */ 394 @Nullable 395 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563") getMaxInboundMessageSize()396 public Integer getMaxInboundMessageSize() { 397 return maxInboundMessageSize; 398 } 399 400 /** 401 * Gets the maximum allowed message size acceptable to send the remote peer. 402 */ 403 @Nullable 404 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563") getMaxOutboundMessageSize()405 public Integer getMaxOutboundMessageSize() { 406 return maxOutboundMessageSize; 407 } 408 409 /** 410 * Copy constructor. 411 */ CallOptions(CallOptions other)412 private CallOptions(CallOptions other) { 413 deadline = other.deadline; 414 authority = other.authority; 415 credentials = other.credentials; 416 executor = other.executor; 417 compressorName = other.compressorName; 418 customOptions = other.customOptions; 419 waitForReady = other.waitForReady; 420 maxInboundMessageSize = other.maxInboundMessageSize; 421 maxOutboundMessageSize = other.maxOutboundMessageSize; 422 streamTracerFactories = other.streamTracerFactories; 423 } 424 425 @Override toString()426 public String toString() { 427 return MoreObjects.toStringHelper(this) 428 .add("deadline", deadline) 429 .add("authority", authority) 430 .add("callCredentials", credentials) 431 .add("executor", executor != null ? executor.getClass() : null) 432 .add("compressorName", compressorName) 433 .add("customOptions", Arrays.deepToString(customOptions)) 434 .add("waitForReady", isWaitForReady()) 435 .add("maxInboundMessageSize", maxInboundMessageSize) 436 .add("maxOutboundMessageSize", maxOutboundMessageSize) 437 .add("streamTracerFactories", streamTracerFactories) 438 .toString(); 439 } 440 } 441