• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 Google LLC
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google LLC nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 package com.google.api.gax.rpc;
31 
32 import com.google.api.core.ApiClock;
33 import com.google.api.core.ApiFunction;
34 import com.google.api.core.BetaApi;
35 import com.google.api.core.NanoClock;
36 import com.google.api.gax.core.CredentialsProvider;
37 import com.google.api.gax.core.ExecutorProvider;
38 import com.google.api.gax.core.FixedCredentialsProvider;
39 import com.google.api.gax.core.FixedExecutorProvider;
40 import com.google.api.gax.core.InstantiatingExecutorProvider;
41 import com.google.api.gax.core.NoCredentialsProvider;
42 import com.google.api.gax.tracing.ApiTracerFactory;
43 import com.google.api.gax.tracing.BaseApiTracerFactory;
44 import com.google.auth.oauth2.QuotaProjectIdProvider;
45 import com.google.common.base.MoreObjects;
46 import com.google.common.base.Preconditions;
47 import java.io.IOException;
48 import java.util.concurrent.Executor;
49 import javax.annotation.Nonnull;
50 import javax.annotation.Nullable;
51 import org.threeten.bp.Duration;
52 
53 /**
54  * A base settings class to configure a client stub class.
55  *
56  * <p>This base class includes settings that are applicable to all services, which includes things
57  * like settings for creating an executor, credentials, transport-specific settings, and identifiers
58  * for http headers.
59  *
60  * <p>If no ExecutorProvider is set, then InstantiatingExecutorProvider will be used, which creates
61  * a default executor.
62  */
63 public abstract class StubSettings<SettingsT extends StubSettings<SettingsT>> {
64 
65   static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-goog-user-project";
66 
67   private final ExecutorProvider backgroundExecutorProvider;
68   private final CredentialsProvider credentialsProvider;
69   private final HeaderProvider headerProvider;
70   private final HeaderProvider internalHeaderProvider;
71   private final TransportChannelProvider transportChannelProvider;
72   private final ApiClock clock;
73   private final String endpoint;
74   private final String mtlsEndpoint;
75   private final String quotaProjectId;
76   @Nullable private final WatchdogProvider streamWatchdogProvider;
77   @Nonnull private final Duration streamWatchdogCheckInterval;
78   @Nonnull private final ApiTracerFactory tracerFactory;
79   // Track if deprecated setExecutorProvider is called
80   private boolean deprecatedExecutorProviderSet;
81 
82   /**
83    * Indicate when creating transport whether it is allowed to use mTLS endpoint instead of the
84    * default endpoint. Only the endpoint set by client libraries is allowed. User provided endpoint
85    * should always be used as it is. Client libraries can set it via the {@link
86    * Builder#setSwitchToMtlsEndpointAllowed} method.
87    */
88   private final boolean switchToMtlsEndpointAllowed;
89 
90   /** Constructs an instance of StubSettings. */
StubSettings(Builder builder)91   protected StubSettings(Builder builder) {
92     this.backgroundExecutorProvider = builder.backgroundExecutorProvider;
93     this.transportChannelProvider = builder.transportChannelProvider;
94     this.credentialsProvider = builder.credentialsProvider;
95     this.headerProvider = builder.headerProvider;
96     this.internalHeaderProvider = builder.internalHeaderProvider;
97     this.clock = builder.clock;
98     this.endpoint = builder.endpoint;
99     this.mtlsEndpoint = builder.mtlsEndpoint;
100     this.switchToMtlsEndpointAllowed = builder.switchToMtlsEndpointAllowed;
101     this.quotaProjectId = builder.quotaProjectId;
102     this.streamWatchdogProvider = builder.streamWatchdogProvider;
103     this.streamWatchdogCheckInterval = builder.streamWatchdogCheckInterval;
104     this.tracerFactory = builder.tracerFactory;
105     this.deprecatedExecutorProviderSet = builder.deprecatedExecutorProviderSet;
106   }
107 
108   /** @deprecated Please use {@link #getBackgroundExecutorProvider()}. */
109   @Deprecated
getExecutorProvider()110   public final ExecutorProvider getExecutorProvider() {
111     return deprecatedExecutorProviderSet ? backgroundExecutorProvider : null;
112   }
113 
getBackgroundExecutorProvider()114   public final ExecutorProvider getBackgroundExecutorProvider() {
115     return backgroundExecutorProvider;
116   }
117 
getTransportChannelProvider()118   public final TransportChannelProvider getTransportChannelProvider() {
119     return transportChannelProvider;
120   }
121 
getCredentialsProvider()122   public final CredentialsProvider getCredentialsProvider() {
123     return credentialsProvider;
124   }
125 
getHeaderProvider()126   public final HeaderProvider getHeaderProvider() {
127     return headerProvider;
128   }
129 
getInternalHeaderProvider()130   protected final HeaderProvider getInternalHeaderProvider() {
131     return internalHeaderProvider;
132   }
133 
getClock()134   public final ApiClock getClock() {
135     return clock;
136   }
137 
getEndpoint()138   public final String getEndpoint() {
139     return endpoint;
140   }
141 
getMtlsEndpoint()142   public final String getMtlsEndpoint() {
143     return mtlsEndpoint;
144   }
145 
146   /** Limit the visibility to this package only since only this package needs it. */
getSwitchToMtlsEndpointAllowed()147   final boolean getSwitchToMtlsEndpointAllowed() {
148     return switchToMtlsEndpointAllowed;
149   }
150 
getQuotaProjectId()151   public final String getQuotaProjectId() {
152     return quotaProjectId;
153   }
154 
155   @Nullable
getStreamWatchdogProvider()156   public final WatchdogProvider getStreamWatchdogProvider() {
157     return streamWatchdogProvider;
158   }
159 
160   @Nonnull
getStreamWatchdogCheckInterval()161   public final Duration getStreamWatchdogCheckInterval() {
162     return streamWatchdogCheckInterval;
163   }
164 
165   /**
166    * Gets the configured {@link ApiTracerFactory} that will be used to generate traces for
167    * operations.
168    */
169   @BetaApi("The surface for tracing is not stable yet and may change in the future.")
170   @Nonnull
getTracerFactory()171   public ApiTracerFactory getTracerFactory() {
172     return tracerFactory;
173   }
174 
175   @Override
toString()176   public String toString() {
177     return MoreObjects.toStringHelper(this)
178         .add("backgroundExecutorProvider", backgroundExecutorProvider)
179         .add("transportChannelProvider", transportChannelProvider)
180         .add("credentialsProvider", credentialsProvider)
181         .add("headerProvider", headerProvider)
182         .add("internalHeaderProvider", internalHeaderProvider)
183         .add("clock", clock)
184         .add("endpoint", endpoint)
185         .add("mtlsEndpoint", mtlsEndpoint)
186         .add("switchToMtlsEndpointAllowed", switchToMtlsEndpointAllowed)
187         .add("quotaProjectId", quotaProjectId)
188         .add("streamWatchdogProvider", streamWatchdogProvider)
189         .add("streamWatchdogCheckInterval", streamWatchdogCheckInterval)
190         .add("tracerFactory", tracerFactory)
191         .toString();
192   }
193 
toBuilder()194   public abstract StubSettings.Builder toBuilder();
195 
196   public abstract static class Builder<
197       SettingsT extends StubSettings<SettingsT>, B extends Builder<SettingsT, B>> {
198 
199     private ExecutorProvider backgroundExecutorProvider;
200     private CredentialsProvider credentialsProvider;
201     private HeaderProvider headerProvider;
202     private HeaderProvider internalHeaderProvider;
203     private TransportChannelProvider transportChannelProvider;
204     private ApiClock clock;
205     private String endpoint;
206     private String mtlsEndpoint;
207     private String quotaProjectId;
208     @Nullable private WatchdogProvider streamWatchdogProvider;
209     @Nonnull private Duration streamWatchdogCheckInterval;
210     @Nonnull private ApiTracerFactory tracerFactory;
211     private boolean deprecatedExecutorProviderSet;
212 
213     /**
214      * Indicate when creating transport whether it is allowed to use mTLS endpoint instead of the
215      * default endpoint. Only the endpoint set by client libraries is allowed. User provided
216      * endpoint should always be used as it is. Client libraries can set it via the {@link
217      * Builder#setSwitchToMtlsEndpointAllowed} method.
218      */
219     private boolean switchToMtlsEndpointAllowed = false;
220 
221     /** Create a builder from a StubSettings object. */
Builder(StubSettings settings)222     protected Builder(StubSettings settings) {
223       this.backgroundExecutorProvider = settings.backgroundExecutorProvider;
224       this.transportChannelProvider = settings.transportChannelProvider;
225       this.credentialsProvider = settings.credentialsProvider;
226       this.headerProvider = settings.headerProvider;
227       this.internalHeaderProvider = settings.internalHeaderProvider;
228       this.clock = settings.clock;
229       this.endpoint = settings.endpoint;
230       this.mtlsEndpoint = settings.mtlsEndpoint;
231       this.switchToMtlsEndpointAllowed = settings.switchToMtlsEndpointAllowed;
232       this.quotaProjectId = settings.quotaProjectId;
233       this.streamWatchdogProvider = settings.streamWatchdogProvider;
234       this.streamWatchdogCheckInterval = settings.streamWatchdogCheckInterval;
235       this.tracerFactory = settings.tracerFactory;
236       this.deprecatedExecutorProviderSet = settings.deprecatedExecutorProviderSet;
237     }
238 
239     /** Get Quota Project ID from Client Context * */
getQuotaProjectIdFromClientContext(ClientContext clientContext)240     private static String getQuotaProjectIdFromClientContext(ClientContext clientContext) {
241       if (clientContext.getQuotaProjectId() != null) {
242         return clientContext.getQuotaProjectId();
243       }
244       if (clientContext.getCredentials() instanceof QuotaProjectIdProvider) {
245         return ((QuotaProjectIdProvider) clientContext.getCredentials()).getQuotaProjectId();
246       }
247       if (clientContext.getHeaders().containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) {
248         return clientContext.getHeaders().get(QUOTA_PROJECT_ID_HEADER_KEY);
249       }
250       if (clientContext.getInternalHeaders().containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) {
251         return clientContext.getInternalHeaders().get(QUOTA_PROJECT_ID_HEADER_KEY);
252       }
253       return null;
254     }
255 
Builder(ClientContext clientContext)256     protected Builder(ClientContext clientContext) {
257       if (clientContext == null) {
258         this.backgroundExecutorProvider = InstantiatingExecutorProvider.newBuilder().build();
259         this.transportChannelProvider = null;
260         this.credentialsProvider = NoCredentialsProvider.create();
261         this.headerProvider = new NoHeaderProvider();
262         this.internalHeaderProvider = new NoHeaderProvider();
263         this.clock = NanoClock.getDefaultClock();
264         this.endpoint = null;
265         this.mtlsEndpoint = null;
266         this.quotaProjectId = null;
267         this.streamWatchdogProvider = InstantiatingWatchdogProvider.create();
268         this.streamWatchdogCheckInterval = Duration.ofSeconds(10);
269         this.tracerFactory = BaseApiTracerFactory.getInstance();
270         this.deprecatedExecutorProviderSet = false;
271       } else {
272         ExecutorProvider fixedExecutorProvider =
273             FixedExecutorProvider.create(clientContext.getExecutor());
274         this.deprecatedExecutorProviderSet = true;
275         this.backgroundExecutorProvider = fixedExecutorProvider;
276         this.transportChannelProvider =
277             FixedTransportChannelProvider.create(clientContext.getTransportChannel());
278         this.credentialsProvider = FixedCredentialsProvider.create(clientContext.getCredentials());
279         this.headerProvider = FixedHeaderProvider.create(clientContext.getHeaders());
280         this.internalHeaderProvider =
281             FixedHeaderProvider.create(clientContext.getInternalHeaders());
282         this.clock = clientContext.getClock();
283         this.endpoint = clientContext.getEndpoint();
284         if (this.endpoint != null) {
285           this.mtlsEndpoint = this.endpoint.replace("googleapis.com", "mtls.googleapis.com");
286         }
287         this.streamWatchdogProvider =
288             FixedWatchdogProvider.create(clientContext.getStreamWatchdog());
289         this.streamWatchdogCheckInterval = clientContext.getStreamWatchdogCheckInterval();
290         this.tracerFactory = clientContext.getTracerFactory();
291         this.quotaProjectId = getQuotaProjectIdFromClientContext(clientContext);
292       }
293     }
294 
Builder()295     protected Builder() {
296       this((ClientContext) null);
297     }
298 
299     @SuppressWarnings("unchecked")
self()300     protected B self() {
301       return (B) this;
302     }
303 
304     /**
305      * Sets the ExecutorProvider to use for getting the executor to use for running asynchronous API
306      * call logic (such as retries and long-running operations), and also to pass to the transport
307      * settings if an executor is needed for the transport and it doesn't have its own executor
308      * provider.
309      *
310      * @deprecated Please use {@link #setBackgroundExecutorProvider(ExecutorProvider)} for setting
311      *     executor to use for running scheduled API call logic. To set executor for {@link
312      *     TransportChannelProvider}, please use {@link
313      *     TransportChannelProvider#withExecutor(Executor)} instead.
314      */
315     @Deprecated
setExecutorProvider(ExecutorProvider executorProvider)316     public B setExecutorProvider(ExecutorProvider executorProvider) {
317       // For backward compatibility, this will set backgroundExecutorProvider and mark
318       // deprecatedExecutorProviderSet to true. In ClientContext#create(), if
319       // TransportChannelProvider doesn't have an executor, and deprecatedExecutorProviderSet is
320       // true, backgroundExecutorProvider will be used as TransportChannelProvider's executor.
321       // After this method is deprecated, TransportChannelProvider's executor can only be set with
322       // TransportChannelProvider#withExecutor.
323       this.deprecatedExecutorProviderSet = true;
324       this.backgroundExecutorProvider = executorProvider;
325       return self();
326     }
327 
328     /**
329      * Sets the executor to use for running scheduled API call logic (such as retries and
330      * long-running operations).
331      */
setBackgroundExecutorProvider(ExecutorProvider backgroundExecutorProvider)332     public B setBackgroundExecutorProvider(ExecutorProvider backgroundExecutorProvider) {
333       this.backgroundExecutorProvider = backgroundExecutorProvider;
334       return self();
335     }
336 
337     /** Sets the CredentialsProvider to use for getting the credentials to make calls with. */
setCredentialsProvider(CredentialsProvider credentialsProvider)338     public B setCredentialsProvider(CredentialsProvider credentialsProvider) {
339       this.credentialsProvider = Preconditions.checkNotNull(credentialsProvider);
340       return self();
341     }
342 
343     /**
344      * Sets the HeaderProvider for getting custom static headers for http requests. The header
345      * provider will be called during client construction only once. The headers returned by the
346      * provider will be cached and supplied as is for each request issued by the constructed client.
347      * Some reserved headers can be overridden (e.g. Content-Type) or merged with the default value
348      * (e.g. User-Agent) by the underlying transport layer.
349      */
setHeaderProvider(HeaderProvider headerProvider)350     public B setHeaderProvider(HeaderProvider headerProvider) {
351       this.headerProvider = headerProvider;
352       if (this.quotaProjectId == null
353           && headerProvider.getHeaders().containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) {
354         this.quotaProjectId = headerProvider.getHeaders().get(QUOTA_PROJECT_ID_HEADER_KEY);
355       }
356       return self();
357     }
358 
359     /**
360      * Sets the HeaderProvider for getting internal (library-defined) static headers for http
361      * requests. The header provider will be called during client construction only once. The
362      * headers returned by the provider will be cached and supplied as is for each request issued by
363      * the constructed client. Some reserved headers can be overridden (e.g. Content-Type) or merged
364      * with the default value (e.g. User-Agent) by the underlying transport layer.
365      */
setInternalHeaderProvider(HeaderProvider internalHeaderProvider)366     protected B setInternalHeaderProvider(HeaderProvider internalHeaderProvider) {
367       this.internalHeaderProvider = internalHeaderProvider;
368       if (this.quotaProjectId == null
369           && internalHeaderProvider.getHeaders().containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) {
370         this.quotaProjectId = internalHeaderProvider.getHeaders().get(QUOTA_PROJECT_ID_HEADER_KEY);
371       }
372       return self();
373     }
374 
375     /**
376      * Sets the TransportProvider to use for getting the transport-specific context to make calls
377      * with.
378      */
setTransportChannelProvider(TransportChannelProvider transportChannelProvider)379     public B setTransportChannelProvider(TransportChannelProvider transportChannelProvider) {
380       this.transportChannelProvider = transportChannelProvider;
381       return self();
382     }
383 
384     /**
385      * Sets the {@link WatchdogProvider} to use for streaming RPC.
386      *
387      * <p>This will default to a {@link InstantiatingWatchdogProvider} if it is not set.
388      */
setStreamWatchdogProvider(@ullable WatchdogProvider streamWatchdogProvider)389     public B setStreamWatchdogProvider(@Nullable WatchdogProvider streamWatchdogProvider) {
390       this.streamWatchdogProvider = streamWatchdogProvider;
391       return self();
392     }
393 
394     /**
395      * Sets the clock to use for retry logic.
396      *
397      * <p>This will default to a system clock if it is not set.
398      */
setClock(ApiClock clock)399     public B setClock(ApiClock clock) {
400       this.clock = clock;
401       return self();
402     }
403 
setEndpoint(String endpoint)404     public B setEndpoint(String endpoint) {
405       this.endpoint = endpoint;
406       this.switchToMtlsEndpointAllowed = false;
407       if (this.endpoint != null && this.mtlsEndpoint == null) {
408         this.mtlsEndpoint = this.endpoint.replace("googleapis.com", "mtls.googleapis.com");
409       }
410       return self();
411     }
412 
setSwitchToMtlsEndpointAllowed(boolean switchToMtlsEndpointAllowed)413     protected B setSwitchToMtlsEndpointAllowed(boolean switchToMtlsEndpointAllowed) {
414       this.switchToMtlsEndpointAllowed = switchToMtlsEndpointAllowed;
415       return self();
416     }
417 
setMtlsEndpoint(String mtlsEndpoint)418     public B setMtlsEndpoint(String mtlsEndpoint) {
419       this.mtlsEndpoint = mtlsEndpoint;
420       return self();
421     }
422 
setQuotaProjectId(String quotaProjectId)423     public B setQuotaProjectId(String quotaProjectId) {
424       this.quotaProjectId = quotaProjectId;
425       return self();
426     }
427 
428     /**
429      * Sets how often the {@link Watchdog} will check ongoing streaming RPCs. Defaults to 10 secs.
430      * Use {@link Duration#ZERO} to disable.
431      */
setStreamWatchdogCheckInterval(@onnull Duration checkInterval)432     public B setStreamWatchdogCheckInterval(@Nonnull Duration checkInterval) {
433       Preconditions.checkNotNull(checkInterval);
434       this.streamWatchdogCheckInterval = checkInterval;
435       return self();
436     }
437 
438     /**
439      * Configures the {@link ApiTracerFactory} that will be used to generate traces.
440      *
441      * @param tracerFactory an instance of {@link ApiTracerFactory} to set.
442      */
443     @BetaApi("The surface for tracing is not stable yet and may change in the future.")
setTracerFactory(@onnull ApiTracerFactory tracerFactory)444     public B setTracerFactory(@Nonnull ApiTracerFactory tracerFactory) {
445       Preconditions.checkNotNull(tracerFactory);
446       this.tracerFactory = tracerFactory;
447       return self();
448     }
449 
450     /** @deprecated Please use {@link #getBackgroundExecutorProvider()}. */
451     @Deprecated
getExecutorProvider()452     public ExecutorProvider getExecutorProvider() {
453       return deprecatedExecutorProviderSet ? backgroundExecutorProvider : null;
454     }
455 
456     /** Gets the ExecutorProvider that was previously set on this Builder. */
getBackgroundExecutorProvider()457     public ExecutorProvider getBackgroundExecutorProvider() {
458       return backgroundExecutorProvider;
459     }
460 
461     /** Gets the TransportProvider that was previously set on this Builder. */
getTransportChannelProvider()462     public TransportChannelProvider getTransportChannelProvider() {
463       return transportChannelProvider;
464     }
465 
466     /** Gets the CredentialsProvider that was previously set on this Builder. */
getCredentialsProvider()467     public CredentialsProvider getCredentialsProvider() {
468       return credentialsProvider;
469     }
470 
471     /** Gets the custom HeaderProvider that was previously set on this Builder. */
getHeaderProvider()472     public HeaderProvider getHeaderProvider() {
473       return headerProvider;
474     }
475 
476     /** Gets the internal HeaderProvider that was previously set on this Builder. */
getInternalHeaderProvider()477     protected HeaderProvider getInternalHeaderProvider() {
478       return internalHeaderProvider;
479     }
480 
481     /** Gets the {@link WatchdogProvider }that was previously set on this Builder. */
482     @Nullable
getStreamWatchdogProvider()483     public WatchdogProvider getStreamWatchdogProvider() {
484       return streamWatchdogProvider;
485     }
486 
487     /** Gets the ApiClock that was previously set on this Builder. */
getClock()488     public ApiClock getClock() {
489       return clock;
490     }
491 
getEndpoint()492     public String getEndpoint() {
493       return endpoint;
494     }
495 
getMtlsEndpoint()496     public String getMtlsEndpoint() {
497       return mtlsEndpoint;
498     }
499 
500     /** Gets the QuotaProjectId that was previously set on this Builder. */
getQuotaProjectId()501     public String getQuotaProjectId() {
502       return quotaProjectId;
503     }
504 
505     @Nonnull
getStreamWatchdogCheckInterval()506     public Duration getStreamWatchdogCheckInterval() {
507       return streamWatchdogCheckInterval;
508     }
509 
510     @BetaApi("The surface for tracing is not stable yet and may change in the future.")
511     @Nonnull
getTracerFactory()512     public ApiTracerFactory getTracerFactory() {
513       return tracerFactory;
514     }
515 
516     /** Applies the given settings updater function to the given method settings builders. */
applyToAllUnaryMethods( Iterable<UnaryCallSettings.Builder<?, ?>> methodSettingsBuilders, ApiFunction<UnaryCallSettings.Builder<?, ?>, Void> settingsUpdater)517     protected static void applyToAllUnaryMethods(
518         Iterable<UnaryCallSettings.Builder<?, ?>> methodSettingsBuilders,
519         ApiFunction<UnaryCallSettings.Builder<?, ?>, Void> settingsUpdater) {
520       for (UnaryCallSettings.Builder<?, ?> settingsBuilder : methodSettingsBuilders) {
521         settingsUpdater.apply(settingsBuilder);
522       }
523     }
524 
build()525     public abstract <B extends StubSettings<B>> StubSettings<B> build() throws IOException;
526 
527     @Override
toString()528     public String toString() {
529       return MoreObjects.toStringHelper(this)
530           .add("backgroundExecutorProvider", backgroundExecutorProvider)
531           .add("transportChannelProvider", transportChannelProvider)
532           .add("credentialsProvider", credentialsProvider)
533           .add("headerProvider", headerProvider)
534           .add("internalHeaderProvider", internalHeaderProvider)
535           .add("clock", clock)
536           .add("endpoint", endpoint)
537           .add("mtlsEndpoint", mtlsEndpoint)
538           .add("switchToMtlsEndpointAllowed", switchToMtlsEndpointAllowed)
539           .add("quotaProjectId", quotaProjectId)
540           .add("streamWatchdogProvider", streamWatchdogProvider)
541           .add("streamWatchdogCheckInterval", streamWatchdogCheckInterval)
542           .add("tracerFactory", tracerFactory)
543           .toString();
544     }
545   }
546 }
547