• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #19533

30 Oct 2024 01:19PM UTC coverage: 84.562% (-0.01%) from 84.573%
#19533

push

github

web-flow
api: Add java.time.Duration overloads to CallOptions, AbstractStub taking TimeUnit and a time value (#11562)

33927 of 40121 relevant lines covered (84.56%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.74
/../api/src/main/java/io/grpc/CallOptions.java
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
import static io.grpc.TimeUtils.convertToNanos;
21

22
import com.google.common.base.MoreObjects;
23
import com.google.common.base.Preconditions;
24
import java.time.Duration;
25
import java.util.ArrayList;
26
import java.util.Arrays;
27
import java.util.Collections;
28
import java.util.List;
29
import java.util.concurrent.Executor;
30
import java.util.concurrent.TimeUnit;
31
import javax.annotation.CheckReturnValue;
32
import javax.annotation.Nullable;
33
import javax.annotation.concurrent.Immutable;
34

35
/**
36
 * The collection of runtime options for a new RPC call.
37
 *
38
 * <p>A field that is not set is {@code null}.
39
 */
40
@Immutable
41
@CheckReturnValue
42
public final class CallOptions {
43
  /**
44
   * A blank {@code CallOptions} that all fields are not set.
45
   */
46
  public static final CallOptions DEFAULT;
47

48
  static {
49
    Builder b = new Builder();
1✔
50
    b.customOptions = new Object[0][2];
1✔
51
    b.streamTracerFactories = Collections.emptyList();
1✔
52
    DEFAULT = b.build();
1✔
53
  }
1✔
54

55
  @Nullable
56
  private final Deadline deadline;
57

58
  @Nullable
59
  private final Executor executor;
60

61
  @Nullable
62
  private final String authority;
63

64
  @Nullable
65
  private final CallCredentials credentials;
66

67
  @Nullable
68
  private final String compressorName;
69

70
  private final Object[][] customOptions;
71

72
  private final List<ClientStreamTracer.Factory> streamTracerFactories;
73

74
  /**
75
   * Opposite to fail fast.
76
   */
77
  @Nullable
78
  private final Boolean waitForReady;
79

80
  @Nullable
81
  private final Integer maxInboundMessageSize;
82
  @Nullable
83
  private final Integer maxOutboundMessageSize;
84
  @Nullable
85
  private final Integer onReadyThreshold;
86

87
  private CallOptions(Builder builder) {
1✔
88
    this.deadline = builder.deadline;
1✔
89
    this.executor = builder.executor;
1✔
90
    this.authority = builder.authority;
1✔
91
    this.credentials = builder.credentials;
1✔
92
    this.compressorName = builder.compressorName;
1✔
93
    this.customOptions = builder.customOptions;
1✔
94
    this.streamTracerFactories = builder.streamTracerFactories;
1✔
95
    this.waitForReady = builder.waitForReady;
1✔
96
    this.maxInboundMessageSize = builder.maxInboundMessageSize;
1✔
97
    this.maxOutboundMessageSize = builder.maxOutboundMessageSize;
1✔
98
    this.onReadyThreshold = builder.onReadyThreshold;
1✔
99
  }
1✔
100

101
  static class Builder {
1✔
102
    Deadline deadline;
103
    Executor executor;
104
    String authority;
105
    CallCredentials credentials;
106
    String compressorName;
107
    Object[][] customOptions;
108
    // Unmodifiable list
109
    List<ClientStreamTracer.Factory> streamTracerFactories;
110
    Boolean waitForReady;
111
    Integer maxInboundMessageSize;
112
    Integer maxOutboundMessageSize;
113
    Integer onReadyThreshold;
114

115
    private CallOptions build() {
116
      return new CallOptions(this);
1✔
117
    }
118
  }
119

120
  /**
121
   * Override the HTTP/2 authority the channel claims to be connecting to. <em>This is not
122
   * generally safe.</em> Overriding allows advanced users to re-use a single Channel for multiple
123
   * services, even if those services are hosted on different domain names. That assumes the
124
   * server is virtually hosting multiple domains and is guaranteed to continue doing so. It is
125
   * rare for a service provider to make such a guarantee. <em>At this time, there is no security
126
   * verification of the overridden value, such as making sure the authority matches the server's
127
   * TLS certificate.</em>
128
   */
129
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767")
130
  public CallOptions withAuthority(@Nullable String authority) {
131
    Builder builder = toBuilder(this);
1✔
132
    builder.authority = authority;
1✔
133
    return builder.build();
1✔
134
  }
135

136
  /**
137
   * Returns a new {@code CallOptions} with the given call credentials.
138
   */
139
  public CallOptions withCallCredentials(@Nullable CallCredentials credentials) {
140
    Builder builder = toBuilder(this);
1✔
141
    builder.credentials = credentials;
1✔
142
    return builder.build();
1✔
143
  }
144

145
  /**
146
   * Sets the compression to use for the call.  The compressor must be a valid name known in the
147
   * {@link CompressorRegistry}.  By default, the "gzip" compressor will be available.
148
   *
149
   * <p>It is only safe to call this if the server supports the compression format chosen. There is
150
   * no negotiation performed; if the server does not support the compression chosen, the call will
151
   * fail.
152
   */
153
  public CallOptions withCompression(@Nullable String compressorName) {
154
    Builder builder = toBuilder(this);
1✔
155
    builder.compressorName = compressorName;
1✔
156
    return builder.build();
1✔
157
  }
158

159
  /**
160
   * Returns a new {@code CallOptions} with the given absolute deadline.
161
   *
162
   * <p>This is mostly used for propagating an existing deadline. {@link #withDeadlineAfter} is the
163
   * recommended way of setting a new deadline,
164
   *
165
   * @param deadline the deadline or {@code null} for unsetting the deadline.
166
   */
167
  public CallOptions withDeadline(@Nullable Deadline deadline) {
168
    Builder builder = toBuilder(this);
1✔
169
    builder.deadline = deadline;
1✔
170
    return builder.build();
1✔
171
  }
172

173
  /**
174
   * Returns a new {@code CallOptions} with a deadline that is after the given {@code duration} from
175
   * now.
176
   */
177
  public CallOptions withDeadlineAfter(long duration, TimeUnit unit) {
178
    return withDeadline(Deadline.after(duration, unit));
1✔
179
  }
180

181
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11657")
182
  public CallOptions withDeadlineAfter(Duration duration) {
183
    return withDeadlineAfter(convertToNanos(duration), TimeUnit.NANOSECONDS);
1✔
184
  }
185

186
  /**
187
   * Returns the deadline or {@code null} if the deadline is not set.
188
   */
189
  @Nullable
190
  public Deadline getDeadline() {
191
    return deadline;
1✔
192
  }
193

194
  /**
195
   * Enables <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">
196
   * 'wait for ready'</a> for the call. Wait-for-ready queues the RPC until a connection is
197
   * available. This may dramatically increase the latency of the RPC, but avoids failing
198
   * "unnecessarily." The default queues the RPC until an attempt to connect has completed, but
199
   * fails RPCs without sending them if unable to connect.
200
   */
201
  public CallOptions withWaitForReady() {
202
    Builder builder = toBuilder(this);
1✔
203
    builder.waitForReady = Boolean.TRUE;
1✔
204
    return builder.build();
1✔
205
  }
206

207
  /**
208
   * Disables 'wait for ready' feature for the call.
209
   * This method should be rarely used because the default is without 'wait for ready'.
210
   */
211
  public CallOptions withoutWaitForReady() {
212
    Builder builder = toBuilder(this);
1✔
213
    builder.waitForReady = Boolean.FALSE;
1✔
214
    return builder.build();
1✔
215
  }
216

217
  /**
218
   * Specifies how many bytes must be queued before the call is
219
   * considered not ready to send more messages.
220
   *
221
   * @param numBytes The number of bytes that must be queued. Must be a
222
   *                 positive integer.
223
   */
224
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11021")
225
  public CallOptions withOnReadyThreshold(int numBytes) {
226
    checkArgument(numBytes > 0, "numBytes must be positive: %s", numBytes);
1✔
227
    Builder builder = toBuilder(this);
1✔
228
    builder.onReadyThreshold = numBytes;
1✔
229
    return builder.build();
1✔
230
  }
231

232
  /**
233
   * Resets to the default number of bytes that must be queued before the
234
   * call will leave the <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">
235
   * 'wait for ready'</a> state.
236
   */
237
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11021")
238
  public CallOptions clearOnReadyThreshold() {
239
    Builder builder = toBuilder(this);
1✔
240
    builder.onReadyThreshold = null;
1✔
241
    return builder.build();
1✔
242
  }
243

244
  /**
245
   * Returns to the default number of bytes that must be queued before the
246
   * call will leave the <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">
247
   * 'wait for ready'</a> state.
248
   *
249
   * @return null if the default threshold is used.
250
   */
251
  @Nullable
252
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11021")
253
  public Integer getOnReadyThreshold() {
254
    return onReadyThreshold;
1✔
255
  }
256

257
  /**
258
   * Returns the compressor's name.
259
   */
260
  @Nullable
261
  public String getCompressor() {
262
    return compressorName;
1✔
263
  }
264

265
  /**
266
   * Override the HTTP/2 authority the channel claims to be connecting to. <em>This is not
267
   * generally safe.</em> Overriding allows advanced users to re-use a single Channel for multiple
268
   * services, even if those services are hosted on different domain names. That assumes the
269
   * server is virtually hosting multiple domains and is guaranteed to continue doing so. It is
270
   * rare for a service provider to make such a guarantee. <em>At this time, there is no security
271
   * verification of the overridden value, such as making sure the authority matches the server's
272
   * TLS certificate.</em>
273
   */
274
  @Nullable
275
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767")
276
  public String getAuthority() {
277
    return authority;
1✔
278
  }
279

280
  /**
281
   * Returns the call credentials.
282
   */
283
  @Nullable
284
  public CallCredentials getCredentials() {
285
    return credentials;
1✔
286
  }
287

288
  /**
289
   * Returns a new {@code CallOptions} with {@code executor} to be used instead of the default
290
   * executor specified with {@link ManagedChannelBuilder#executor}.
291
   */
292
  public CallOptions withExecutor(@Nullable Executor executor) {
293
    Builder builder = toBuilder(this);
1✔
294
    builder.executor = executor;
1✔
295
    return builder.build();
1✔
296
  }
297

298
  /**
299
   * Returns a new {@code CallOptions} with a {@code ClientStreamTracerFactory} in addition to
300
   * the existing factories.
301
   *
302
   * <p>This method doesn't replace existing factories, or try to de-duplicate factories.
303
   */
304
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861")
305
  public CallOptions withStreamTracerFactory(ClientStreamTracer.Factory factory) {
306
    ArrayList<ClientStreamTracer.Factory> newList =
1✔
307
        new ArrayList<>(streamTracerFactories.size() + 1);
1✔
308
    newList.addAll(streamTracerFactories);
1✔
309
    newList.add(factory);
1✔
310
    Builder builder = toBuilder(this);
1✔
311
    builder.streamTracerFactories = Collections.unmodifiableList(newList);
1✔
312
    return builder.build();
1✔
313
  }
314

315
  /**
316
   * Returns an immutable list of {@code ClientStreamTracerFactory}s.
317
   */
318
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861")
319
  public List<ClientStreamTracer.Factory> getStreamTracerFactories() {
320
    return streamTracerFactories;
1✔
321
  }
322

323
  /**
324
   * Key for a key-value pair. Uses reference equality.
325
   */
326
  public static final class Key<T> {
327
    private final String debugString;
328
    private final T defaultValue;
329

330
    private Key(String debugString, T defaultValue) {
1✔
331
      this.debugString = debugString;
1✔
332
      this.defaultValue = defaultValue;
1✔
333
    }
1✔
334

335
    /**
336
     * Returns the user supplied default value for this key.
337
     */
338
    public T getDefault() {
339
      return defaultValue;
×
340
    }
341

342
    @Override
343
    public String toString() {
344
      return debugString;
1✔
345
    }
346

347
    /**
348
     * Factory method for creating instances of {@link Key}.
349
     *
350
     * @param debugString a string used to describe this key, used for debugging.
351
     * @param defaultValue default value to return when value for key not set
352
     * @param <T> Key type
353
     * @return Key object
354
     * @deprecated Use {@link #create} or {@link #createWithDefault} instead. This method will
355
     *     be removed.
356
     */
357
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869")
358
    @Deprecated
359
    public static <T> Key<T> of(String debugString, T defaultValue) {
360
      Preconditions.checkNotNull(debugString, "debugString");
×
361
      return new Key<>(debugString, defaultValue);
×
362
    }
363

364
    /**
365
     * Factory method for creating instances of {@link Key}. The default value of the
366
     * key is {@code null}.
367
     *
368
     * @param debugString a debug string that describes this key.
369
     * @param <T> Key type
370
     * @return Key object
371
     * @since 1.13.0
372
     */
373
    public static <T> Key<T> create(String debugString) {
374
      Preconditions.checkNotNull(debugString, "debugString");
1✔
375
      return new Key<>(debugString, /*defaultValue=*/ null);
1✔
376
    }
377

378
    /**
379
     * Factory method for creating instances of {@link Key}.
380
     *
381
     * @param debugString a debug string that describes this key.
382
     * @param defaultValue default value to return when value for key not set
383
     * @param <T> Key type
384
     * @return Key object
385
     * @since 1.13.0
386
     */
387
    public static <T> Key<T> createWithDefault(String debugString, T defaultValue) {
388
      Preconditions.checkNotNull(debugString, "debugString");
1✔
389
      return new Key<>(debugString, defaultValue);
1✔
390
    }
391
  }
392

393
  /**
394
   * Sets a custom option. Any existing value for the key is overwritten.
395
   *
396
   * @param key The option key
397
   * @param value The option value.
398
   * @since 1.13.0
399
   */
400
  public <T> CallOptions withOption(Key<T> key, T value) {
401
    Preconditions.checkNotNull(key, "key");
1✔
402
    Preconditions.checkNotNull(value, "value");
1✔
403

404
    Builder builder = toBuilder(this);
1✔
405
    int existingIdx = -1;
1✔
406
    for (int i = 0; i < customOptions.length; i++) {
1✔
407
      if (key.equals(customOptions[i][0])) {
1✔
408
        existingIdx = i;
1✔
409
        break;
1✔
410
      }
411
    }
412

413
    builder.customOptions = new Object[customOptions.length + (existingIdx == -1 ? 1 : 0)][2];
1✔
414
    System.arraycopy(customOptions, 0, builder.customOptions, 0, customOptions.length);
1✔
415

416
    if (existingIdx == -1) {
1✔
417
      // Add a new option
418
      builder.customOptions[customOptions.length] = new Object[] {key, value};
1✔
419
    } else {
420
      // Replace an existing option
421
      builder.customOptions[existingIdx] = new Object[] {key, value};
1✔
422
    }
423

424
    return builder.build();
1✔
425
  }
426

427
  /**
428
   * Get the value for a custom option or its inherent default.
429
   * @param key Key identifying option
430
   */
431
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869")
432
  @SuppressWarnings("unchecked")
433
  public <T> T getOption(Key<T> key) {
434
    Preconditions.checkNotNull(key, "key");
1✔
435
    for (int i = 0; i < customOptions.length; i++) {
1✔
436
      if (key.equals(customOptions[i][0])) {
1✔
437
        return (T) customOptions[i][1];
1✔
438
      }
439
    }
440
    return key.defaultValue;
1✔
441
  }
442

443
  /**
444
   * Returns the executor override to use for this specific call, or {@code null} if there is no
445
   * override. The executor is only for servicing this one call, so is not safe to use after
446
   * {@link ClientCall.Listener#onClose}.
447
   */
448
  @Nullable
449
  public Executor getExecutor() {
450
    return executor;
1✔
451
  }
452

453
  /**
454
   * Returns whether <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">
455
   * 'wait for ready'</a> option is enabled for the call. 'Fail fast' is the default option for gRPC
456
   * calls and 'wait for ready' is the opposite to it.
457
   */
458
  public boolean isWaitForReady() {
459
    return Boolean.TRUE.equals(waitForReady);
1✔
460
  }
461

462
  Boolean getWaitForReady() {
463
    return waitForReady;
1✔
464
  }
465

466
  /**
467
   * Sets the maximum allowed message size acceptable from the remote peer.  If unset, this will
468
   * default to the value set on the {@link ManagedChannelBuilder#maxInboundMessageSize(int)}.
469
   */
470
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
471
  public CallOptions withMaxInboundMessageSize(int maxSize) {
472
    checkArgument(maxSize >= 0, "invalid maxsize %s", maxSize);
1✔
473
    Builder builder = toBuilder(this);
1✔
474
    builder.maxInboundMessageSize = maxSize;
1✔
475
    return builder.build();
1✔
476
  }
477

478
  /**
479
   * Sets the maximum allowed message size acceptable sent to the remote peer.
480
   */
481
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
482
  public CallOptions withMaxOutboundMessageSize(int maxSize) {
483
    checkArgument(maxSize >= 0, "invalid maxsize %s", maxSize);
1✔
484
    Builder builder = toBuilder(this);
1✔
485
    builder.maxOutboundMessageSize = maxSize;
1✔
486
    return builder.build();
1✔
487
  }
488

489
  /**
490
   * Gets the maximum allowed message size acceptable from the remote peer.
491
   */
492
  @Nullable
493
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
494
  public Integer getMaxInboundMessageSize() {
495
    return maxInboundMessageSize;
1✔
496
  }
497

498
  /**
499
   * Gets the maximum allowed message size acceptable to send the remote peer.
500
   */
501
  @Nullable
502
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
503
  public Integer getMaxOutboundMessageSize() {
504
    return maxOutboundMessageSize;
1✔
505
  }
506

507
  /**
508
   * Copy CallOptions.
509
   */
510
  private static Builder toBuilder(CallOptions other) {
511
    Builder builder = new Builder();
1✔
512
    builder.deadline = other.deadline;
1✔
513
    builder.executor = other.executor;
1✔
514
    builder.authority = other.authority;
1✔
515
    builder.credentials = other.credentials;
1✔
516
    builder.compressorName = other.compressorName;
1✔
517
    builder.customOptions = other.customOptions;
1✔
518
    builder.streamTracerFactories = other.streamTracerFactories;
1✔
519
    builder.waitForReady = other.waitForReady;
1✔
520
    builder.maxInboundMessageSize = other.maxInboundMessageSize;
1✔
521
    builder.maxOutboundMessageSize = other.maxOutboundMessageSize;
1✔
522
    builder.onReadyThreshold = other.onReadyThreshold;
1✔
523
    return builder;
1✔
524
  }
525

526
  @Override
527
  public String toString() {
528
    return MoreObjects.toStringHelper(this)
1✔
529
        .add("deadline", deadline)
1✔
530
        .add("authority", authority)
1✔
531
        .add("callCredentials", credentials)
1✔
532
        .add("executor", executor != null ? executor.getClass() : null)
1✔
533
        .add("compressorName", compressorName)
1✔
534
        .add("customOptions", Arrays.deepToString(customOptions))
1✔
535
        .add("waitForReady", isWaitForReady())
1✔
536
        .add("maxInboundMessageSize", maxInboundMessageSize)
1✔
537
        .add("maxOutboundMessageSize", maxOutboundMessageSize)
1✔
538
        .add("onReadyThreshold", onReadyThreshold)
1✔
539
        .add("streamTracerFactories", streamTracerFactories)
1✔
540
        .toString();
1✔
541
  }
542
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc