• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
45 
46   static {
47     Builder b = new Builder();
48     b.customOptions = new Object[0][2];
49     b.streamTracerFactories = Collections.emptyList();
50     DEFAULT = b.build();
51   }
52 
53   @Nullable
54   private final Deadline deadline;
55 
56   @Nullable
57   private final Executor executor;
58 
59   @Nullable
60   private final String authority;
61 
62   @Nullable
63   private final CallCredentials credentials;
64 
65   @Nullable
66   private final String compressorName;
67 
68   private final Object[][] customOptions;
69 
70   private final List<ClientStreamTracer.Factory> streamTracerFactories;
71 
72   /**
73    * Opposite to fail fast.
74    */
75   @Nullable
76   private final Boolean waitForReady;
77 
78   @Nullable
79   private final Integer maxInboundMessageSize;
80   @Nullable
81   private final Integer maxOutboundMessageSize;
82 
CallOptions(Builder builder)83   private CallOptions(Builder builder) {
84     this.deadline = builder.deadline;
85     this.executor = builder.executor;
86     this.authority = builder.authority;
87     this.credentials = builder.credentials;
88     this.compressorName = builder.compressorName;
89     this.customOptions = builder.customOptions;
90     this.streamTracerFactories = builder.streamTracerFactories;
91     this.waitForReady = builder.waitForReady;
92     this.maxInboundMessageSize = builder.maxInboundMessageSize;
93     this.maxOutboundMessageSize = builder.maxOutboundMessageSize;
94   }
95 
96   static class Builder {
97     Deadline deadline;
98     Executor executor;
99     String authority;
100     CallCredentials credentials;
101     String compressorName;
102     Object[][] customOptions;
103     // Unmodifiable list
104     List<ClientStreamTracer.Factory> streamTracerFactories;
105     Boolean waitForReady;
106     Integer maxInboundMessageSize;
107     Integer maxOutboundMessageSize;
108 
build()109     private CallOptions build() {
110       return new CallOptions(this);
111     }
112   }
113 
114   /**
115    * Override the HTTP/2 authority the channel claims to be connecting to. <em>This is not
116    * generally safe.</em> Overriding allows advanced users to re-use a single Channel for multiple
117    * services, even if those services are hosted on different domain names. That assumes the
118    * server is virtually hosting multiple domains and is guaranteed to continue doing so. It is
119    * rare for a service provider to make such a guarantee. <em>At this time, there is no security
120    * verification of the overridden value, such as making sure the authority matches the server's
121    * TLS certificate.</em>
122    */
123   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767")
withAuthority(@ullable String authority)124   public CallOptions withAuthority(@Nullable String authority) {
125     Builder builder = toBuilder(this);
126     builder.authority = authority;
127     return builder.build();
128   }
129 
130   /**
131    * Returns a new {@code CallOptions} with the given call credentials.
132    */
withCallCredentials(@ullable CallCredentials credentials)133   public CallOptions withCallCredentials(@Nullable CallCredentials credentials) {
134     Builder builder = toBuilder(this);
135     builder.credentials = credentials;
136     return builder.build();
137   }
138 
139   /**
140    * Sets the compression to use for the call.  The compressor must be a valid name known in the
141    * {@link CompressorRegistry}.  By default, the "gzip" compressor will be available.
142    *
143    * <p>It is only safe to call this if the server supports the compression format chosen. There is
144    * no negotiation performed; if the server does not support the compression chosen, the call will
145    * fail.
146    */
withCompression(@ullable String compressorName)147   public CallOptions withCompression(@Nullable String compressorName) {
148     Builder builder = toBuilder(this);
149     builder.compressorName = compressorName;
150     return builder.build();
151   }
152 
153   /**
154    * Returns a new {@code CallOptions} with the given absolute deadline.
155    *
156    * <p>This is mostly used for propagating an existing deadline. {@link #withDeadlineAfter} is the
157    * recommended way of setting a new deadline,
158    *
159    * @param deadline the deadline or {@code null} for unsetting the deadline.
160    */
withDeadline(@ullable Deadline deadline)161   public CallOptions withDeadline(@Nullable Deadline deadline) {
162     Builder builder = toBuilder(this);
163     builder.deadline = deadline;
164     return builder.build();
165   }
166 
167   /**
168    * Returns a new {@code CallOptions} with a deadline that is after the given {@code duration} from
169    * now.
170    */
withDeadlineAfter(long duration, TimeUnit unit)171   public CallOptions withDeadlineAfter(long duration, TimeUnit unit) {
172     return withDeadline(Deadline.after(duration, unit));
173   }
174 
175   /**
176    * Returns the deadline or {@code null} if the deadline is not set.
177    */
178   @Nullable
getDeadline()179   public Deadline getDeadline() {
180     return deadline;
181   }
182 
183   /**
184    * Enables <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">
185    * 'wait for ready'</a> for the call. Wait-for-ready queues the RPC until a connection is
186    * available. This may dramatically increase the latency of the RPC, but avoids failing
187    * "unnecessarily." The default queues the RPC until an attempt to connect has completed, but
188    * fails RPCs without sending them if unable to connect.
189    */
withWaitForReady()190   public CallOptions withWaitForReady() {
191     Builder builder = toBuilder(this);
192     builder.waitForReady = Boolean.TRUE;
193     return builder.build();
194   }
195 
196   /**
197    * Disables 'wait for ready' feature for the call.
198    * This method should be rarely used because the default is without 'wait for ready'.
199    */
withoutWaitForReady()200   public CallOptions withoutWaitForReady() {
201     Builder builder = toBuilder(this);
202     builder.waitForReady = Boolean.FALSE;
203     return builder.build();
204   }
205 
206   /**
207    * Returns the compressor's name.
208    */
209   @Nullable
getCompressor()210   public String getCompressor() {
211     return compressorName;
212   }
213 
214   /**
215    * Override the HTTP/2 authority the channel claims to be connecting to. <em>This is not
216    * generally safe.</em> Overriding allows advanced users to re-use a single Channel for multiple
217    * services, even if those services are hosted on different domain names. That assumes the
218    * server is virtually hosting multiple domains and is guaranteed to continue doing so. It is
219    * rare for a service provider to make such a guarantee. <em>At this time, there is no security
220    * verification of the overridden value, such as making sure the authority matches the server's
221    * TLS certificate.</em>
222    */
223   @Nullable
224   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767")
getAuthority()225   public String getAuthority() {
226     return authority;
227   }
228 
229   /**
230    * Returns the call credentials.
231    */
232   @Nullable
getCredentials()233   public CallCredentials getCredentials() {
234     return credentials;
235   }
236 
237   /**
238    * Returns a new {@code CallOptions} with {@code executor} to be used instead of the default
239    * executor specified with {@link ManagedChannelBuilder#executor}.
240    */
withExecutor(@ullable Executor executor)241   public CallOptions withExecutor(@Nullable Executor executor) {
242     Builder builder = toBuilder(this);
243     builder.executor = executor;
244     return builder.build();
245   }
246 
247   /**
248    * Returns a new {@code CallOptions} with a {@code ClientStreamTracerFactory} in addition to
249    * the existing factories.
250    *
251    * <p>This method doesn't replace existing factories, or try to de-duplicate factories.
252    */
253   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861")
withStreamTracerFactory(ClientStreamTracer.Factory factory)254   public CallOptions withStreamTracerFactory(ClientStreamTracer.Factory factory) {
255     ArrayList<ClientStreamTracer.Factory> newList =
256         new ArrayList<>(streamTracerFactories.size() + 1);
257     newList.addAll(streamTracerFactories);
258     newList.add(factory);
259     Builder builder = toBuilder(this);
260     builder.streamTracerFactories = Collections.unmodifiableList(newList);
261     return builder.build();
262   }
263 
264   /**
265    * Returns an immutable list of {@code ClientStreamTracerFactory}s.
266    */
267   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861")
getStreamTracerFactories()268   public List<ClientStreamTracer.Factory> getStreamTracerFactories() {
269     return streamTracerFactories;
270   }
271 
272   /**
273    * Key for a key-value pair. Uses reference equality.
274    */
275   public static final class Key<T> {
276     private final String debugString;
277     private final T defaultValue;
278 
Key(String debugString, T defaultValue)279     private Key(String debugString, T defaultValue) {
280       this.debugString = debugString;
281       this.defaultValue = defaultValue;
282     }
283 
284     /**
285      * Returns the user supplied default value for this key.
286      */
getDefault()287     public T getDefault() {
288       return defaultValue;
289     }
290 
291     @Override
toString()292     public String toString() {
293       return debugString;
294     }
295 
296     /**
297      * Factory method for creating instances of {@link Key}.
298      *
299      * @param debugString a string used to describe this key, used for debugging.
300      * @param defaultValue default value to return when value for key not set
301      * @param <T> Key type
302      * @return Key object
303      * @deprecated Use {@link #create} or {@link #createWithDefault} instead. This method will
304      *     be removed.
305      */
306     @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869")
307     @Deprecated
of(String debugString, T defaultValue)308     public static <T> Key<T> of(String debugString, T defaultValue) {
309       Preconditions.checkNotNull(debugString, "debugString");
310       return new Key<>(debugString, defaultValue);
311     }
312 
313     /**
314      * Factory method for creating instances of {@link Key}. The default value of the
315      * key is {@code null}.
316      *
317      * @param debugString a debug string that describes this key.
318      * @param <T> Key type
319      * @return Key object
320      * @since 1.13.0
321      */
create(String debugString)322     public static <T> Key<T> create(String debugString) {
323       Preconditions.checkNotNull(debugString, "debugString");
324       return new Key<>(debugString, /*defaultValue=*/ null);
325     }
326 
327     /**
328      * Factory method for creating instances of {@link Key}.
329      *
330      * @param debugString a debug string that describes this key.
331      * @param defaultValue default value to return when value for key not set
332      * @param <T> Key type
333      * @return Key object
334      * @since 1.13.0
335      */
createWithDefault(String debugString, T defaultValue)336     public static <T> Key<T> createWithDefault(String debugString, T defaultValue) {
337       Preconditions.checkNotNull(debugString, "debugString");
338       return new Key<>(debugString, defaultValue);
339     }
340   }
341 
342   /**
343    * Sets a custom option. Any existing value for the key is overwritten.
344    *
345    * @param key The option key
346    * @param value The option value.
347    * @since 1.13.0
348    */
withOption(Key<T> key, T value)349   public <T> CallOptions withOption(Key<T> key, T value) {
350     Preconditions.checkNotNull(key, "key");
351     Preconditions.checkNotNull(value, "value");
352 
353     Builder builder = toBuilder(this);
354     int existingIdx = -1;
355     for (int i = 0; i < customOptions.length; i++) {
356       if (key.equals(customOptions[i][0])) {
357         existingIdx = i;
358         break;
359       }
360     }
361 
362     builder.customOptions = new Object[customOptions.length + (existingIdx == -1 ? 1 : 0)][2];
363     System.arraycopy(customOptions, 0, builder.customOptions, 0, customOptions.length);
364 
365     if (existingIdx == -1) {
366       // Add a new option
367       builder.customOptions[customOptions.length] = new Object[] {key, value};
368     } else {
369       // Replace an existing option
370       builder.customOptions[existingIdx] = new Object[] {key, value};
371     }
372 
373     return builder.build();
374   }
375 
376   /**
377    * Get the value for a custom option or its inherent default.
378    * @param key Key identifying option
379    */
380   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869")
381   @SuppressWarnings("unchecked")
getOption(Key<T> key)382   public <T> T getOption(Key<T> key) {
383     Preconditions.checkNotNull(key, "key");
384     for (int i = 0; i < customOptions.length; i++) {
385       if (key.equals(customOptions[i][0])) {
386         return (T) customOptions[i][1];
387       }
388     }
389     return key.defaultValue;
390   }
391 
392   /**
393    * Returns the executor override to use for this specific call, or {@code null} if there is no
394    * override. The executor is only for servicing this one call, so is not safe to use after
395    * {@link ClientCall.Listener#onClose}.
396    */
397   @Nullable
getExecutor()398   public Executor getExecutor() {
399     return executor;
400   }
401 
402   /**
403    * Returns whether <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">
404    * 'wait for ready'</a> option is enabled for the call. 'Fail fast' is the default option for gRPC
405    * calls and 'wait for ready' is the opposite to it.
406    */
isWaitForReady()407   public boolean isWaitForReady() {
408     return Boolean.TRUE.equals(waitForReady);
409   }
410 
getWaitForReady()411   Boolean getWaitForReady() {
412     return waitForReady;
413   }
414 
415   /**
416    * Sets the maximum allowed message size acceptable from the remote peer.  If unset, this will
417    * default to the value set on the {@link ManagedChannelBuilder#maxInboundMessageSize(int)}.
418    */
419   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
withMaxInboundMessageSize(int maxSize)420   public CallOptions withMaxInboundMessageSize(int maxSize) {
421     checkArgument(maxSize >= 0, "invalid maxsize %s", maxSize);
422     Builder builder = toBuilder(this);
423     builder.maxInboundMessageSize = maxSize;
424     return builder.build();
425   }
426 
427   /**
428    * Sets the maximum allowed message size acceptable sent to the remote peer.
429    */
430   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
withMaxOutboundMessageSize(int maxSize)431   public CallOptions withMaxOutboundMessageSize(int maxSize) {
432     checkArgument(maxSize >= 0, "invalid maxsize %s", maxSize);
433     Builder builder = toBuilder(this);
434     builder.maxOutboundMessageSize = maxSize;
435     return builder.build();
436   }
437 
438   /**
439    * Gets the maximum allowed message size acceptable from the remote peer.
440    */
441   @Nullable
442   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
getMaxInboundMessageSize()443   public Integer getMaxInboundMessageSize() {
444     return maxInboundMessageSize;
445   }
446 
447   /**
448    * Gets the maximum allowed message size acceptable to send the remote peer.
449    */
450   @Nullable
451   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563")
getMaxOutboundMessageSize()452   public Integer getMaxOutboundMessageSize() {
453     return maxOutboundMessageSize;
454   }
455 
456   /**
457    * Copy CallOptions.
458    */
toBuilder(CallOptions other)459   private static Builder toBuilder(CallOptions other) {
460     Builder builder = new Builder();
461     builder.deadline = other.deadline;
462     builder.executor = other.executor;
463     builder.authority = other.authority;
464     builder.credentials = other.credentials;
465     builder.compressorName = other.compressorName;
466     builder.customOptions = other.customOptions;
467     builder.streamTracerFactories = other.streamTracerFactories;
468     builder.waitForReady = other.waitForReady;
469     builder.maxInboundMessageSize = other.maxInboundMessageSize;
470     builder.maxOutboundMessageSize = other.maxOutboundMessageSize;
471     return builder;
472   }
473 
474   @Override
toString()475   public String toString() {
476     return MoreObjects.toStringHelper(this)
477         .add("deadline", deadline)
478         .add("authority", authority)
479         .add("callCredentials", credentials)
480         .add("executor", executor != null ? executor.getClass() : null)
481         .add("compressorName", compressorName)
482         .add("customOptions", Arrays.deepToString(customOptions))
483         .add("waitForReady", isWaitForReady())
484         .add("maxInboundMessageSize", maxInboundMessageSize)
485         .add("maxOutboundMessageSize", maxOutboundMessageSize)
486         .add("streamTracerFactories", streamTracerFactories)
487         .toString();
488   }
489 }
490