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

grpc / grpc-java / #20353

07 Jul 2026 10:24AM UTC coverage: 89.104% (-0.02%) from 89.122%
#20353

push

github

web-flow
enable child channel plugins (#12578)

Implements https://github.com/grpc/proposal/pull/529

38027 of 42677 relevant lines covered (89.1%)

0.89 hits per line

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

91.79
/../core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java
1
/*
2
 * Copyright 2020 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.internal;
18

19
import static com.google.common.base.Preconditions.checkArgument;
20
import static com.google.common.base.Preconditions.checkNotNull;
21
import static io.grpc.internal.UriWrapper.wrap;
22

23
import com.google.common.annotations.VisibleForTesting;
24
import com.google.common.base.Preconditions;
25
import com.google.common.util.concurrent.MoreExecutors;
26
import com.google.errorprone.annotations.DoNotCall;
27
import io.grpc.Attributes;
28
import io.grpc.BinaryLog;
29
import io.grpc.CallCredentials;
30
import io.grpc.CallOptions;
31
import io.grpc.Channel;
32
import io.grpc.ChannelConfigurator;
33
import io.grpc.ChannelCredentials;
34
import io.grpc.ClientCall;
35
import io.grpc.ClientInterceptor;
36
import io.grpc.ClientTransportFilter;
37
import io.grpc.CompressorRegistry;
38
import io.grpc.DecompressorRegistry;
39
import io.grpc.EquivalentAddressGroup;
40
import io.grpc.InternalChannelz;
41
import io.grpc.InternalConfiguratorRegistry;
42
import io.grpc.InternalFeatureFlags;
43
import io.grpc.ManagedChannel;
44
import io.grpc.ManagedChannelBuilder;
45
import io.grpc.MethodDescriptor;
46
import io.grpc.MetricSink;
47
import io.grpc.NameResolver;
48
import io.grpc.NameResolverProvider;
49
import io.grpc.NameResolverRegistry;
50
import io.grpc.ProxyDetector;
51
import io.grpc.StatusOr;
52
import io.grpc.Uri;
53
import java.lang.reflect.InvocationTargetException;
54
import java.lang.reflect.Method;
55
import java.net.SocketAddress;
56
import java.net.URI;
57
import java.net.URISyntaxException;
58
import java.util.ArrayList;
59
import java.util.Arrays;
60
import java.util.Collection;
61
import java.util.Collections;
62
import java.util.IdentityHashMap;
63
import java.util.LinkedHashMap;
64
import java.util.List;
65
import java.util.Map;
66
import java.util.concurrent.Executor;
67
import java.util.concurrent.TimeUnit;
68
import java.util.logging.Level;
69
import java.util.logging.Logger;
70
import java.util.regex.Pattern;
71
import javax.annotation.Nullable;
72

73
/**
74
 * Default managed channel builder, for usage in Transport implementations.
75
 */
76
public final class ManagedChannelImplBuilder
77
    extends ManagedChannelBuilder<ManagedChannelImplBuilder> {
78
  private static final String DIRECT_ADDRESS_SCHEME = "directaddress";
79

80
  private static final Logger log = Logger.getLogger(ManagedChannelImplBuilder.class.getName());
1✔
81

82
  @DoNotCall("ClientTransportFactoryBuilder is required, use a constructor")
83
  public static ManagedChannelBuilder<?> forAddress(String name, int port) {
84
    throw new UnsupportedOperationException(
×
85
        "ClientTransportFactoryBuilder is required, use a constructor");
86
  }
87

88
  @DoNotCall("ClientTransportFactoryBuilder is required, use a constructor")
89
  public static ManagedChannelBuilder<?> forTarget(String target) {
90
    throw new UnsupportedOperationException(
×
91
        "ClientTransportFactoryBuilder is required, use a constructor");
92
  }
93

94
  /**
95
   * An idle timeout larger than this would disable idle mode.
96
   */
97
  @VisibleForTesting
98
  static final long IDLE_MODE_MAX_TIMEOUT_DAYS = 30;
99

100
  /**
101
   * The default idle timeout.
102
   */
103
  @VisibleForTesting
104
  static final long IDLE_MODE_DEFAULT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(30);
1✔
105

106
  /**
107
   * An idle timeout smaller than this would be capped to it.
108
   */
109
  static final long IDLE_MODE_MIN_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(1);
1✔
110

111
  private static final ObjectPool<? extends Executor> DEFAULT_EXECUTOR_POOL =
1✔
112
      SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR);
1✔
113

114
  private static final DecompressorRegistry DEFAULT_DECOMPRESSOR_REGISTRY =
115
      DecompressorRegistry.getDefaultInstance();
1✔
116

117
  private static final CompressorRegistry DEFAULT_COMPRESSOR_REGISTRY =
118
      CompressorRegistry.getDefaultInstance();
1✔
119

120
  private static final long DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES = 1L << 24;  // 16M
121
  private static final long DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES = 1L << 20; // 1M
122

123
  // Matching this pattern means the target string is a URI target or at least intended to be one.
124
  // A URI target must be an absolute hierarchical URI.
125
  // From RFC 2396: scheme = alpha *( alpha | digit | "+" | "-" | "." )
126
  @VisibleForTesting
127
  static final Pattern URI_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9+.-]*:/.*");
1✔
128

129
  private static final Method GET_CLIENT_INTERCEPTOR_METHOD;
130

131
  static {
132
    Method getClientInterceptorMethod = null;
1✔
133
    try {
134
      Class<?> censusStatsAccessor =
1✔
135
          Class.forName("io.grpc.census.InternalCensusStatsAccessor");
1✔
136
      getClientInterceptorMethod =
1✔
137
          censusStatsAccessor.getDeclaredMethod(
1✔
138
              "getClientInterceptor",
139
              boolean.class,
140
              boolean.class,
141
              boolean.class,
142
              boolean.class);
143
    } catch (ClassNotFoundException e) {
1✔
144
      // Replace these separate catch statements with multicatch when Android min-API >= 19
145
      log.log(Level.FINE, "Unable to apply census stats", e);
1✔
146
    } catch (NoSuchMethodException e) {
×
147
      log.log(Level.FINE, "Unable to apply census stats", e);
×
148
    }
1✔
149
    GET_CLIENT_INTERCEPTOR_METHOD = getClientInterceptorMethod;
1✔
150
  }
1✔
151

152

153
  ChannelConfigurator channelConfigurator = builder -> { };
1✔
154

155
  ObjectPool<? extends Executor> executorPool = DEFAULT_EXECUTOR_POOL;
1✔
156

157
  ObjectPool<? extends Executor> offloadExecutorPool = DEFAULT_EXECUTOR_POOL;
1✔
158

159
  private final List<ClientInterceptor> interceptors = new ArrayList<>();
1✔
160
  NameResolverRegistry nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
1✔
161

162
  @Nullable
163
  NameResolverProvider nameResolverProvider;
164

165
  final List<ClientTransportFilter> transportFilters = new ArrayList<>();
1✔
166

167
  final String target;
168
  @Nullable
169
  final ChannelCredentials channelCredentials;
170
  @Nullable
171
  final CallCredentials callCredentials;
172
  @Nullable
173
  IdentityHashMap<NameResolver.Args.Key<?>, Object> nameResolverCustomArgs;
174

175
  @Nullable
176
  private final SocketAddress directServerAddress;
177

178
  @Nullable
179
  String userAgent;
180

181
  @Nullable
182
  String authorityOverride;
183

184
  String defaultLbPolicy = GrpcUtil.DEFAULT_LB_POLICY;
1✔
185

186
  boolean fullStreamDecompression;
187

188
  DecompressorRegistry decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
1✔
189

190
  CompressorRegistry compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
1✔
191

192
  long idleTimeoutMillis = IDLE_MODE_DEFAULT_TIMEOUT_MILLIS;
1✔
193

194
  int maxRetryAttempts = 5;
1✔
195
  int maxHedgedAttempts = 5;
1✔
196
  long retryBufferSize = DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES;
1✔
197
  long perRpcBufferLimit = DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES;
1✔
198
  boolean retryEnabled = true;
1✔
199

200
  InternalChannelz channelz = InternalChannelz.instance();
1✔
201
  int maxTraceEvents;
202

203
  @Nullable
204
  Map<String, ?> defaultServiceConfig;
205
  boolean lookUpServiceConfig = true;
1✔
206

207
  @Nullable
208
  BinaryLog binlog;
209

210
  @Nullable
211
  ProxyDetector proxyDetector;
212

213
  private boolean authorityCheckerDisabled;
214
  private boolean statsEnabled = true;
1✔
215
  private boolean recordStartedRpcs = true;
1✔
216
  private boolean recordFinishedRpcs = true;
1✔
217
  private boolean recordRealTimeMetrics = false;
1✔
218
  private boolean recordRetryMetrics = true;
1✔
219
  private boolean tracingEnabled = true;
1✔
220
  List<MetricSink> metricSinks = new ArrayList<>();
1✔
221

222
  /**
223
   * An interface for Transport implementors to provide the {@link ClientTransportFactory}
224
   * appropriate for the channel.
225
   */
226
  public interface ClientTransportFactoryBuilder {
227
    ClientTransportFactory buildClientTransportFactory();
228
  }
229

230
  /**
231
   * Convenience ClientTransportFactoryBuilder, throws UnsupportedOperationException().
232
   */
233
  public static class UnsupportedClientTransportFactoryBuilder implements
1✔
234
      ClientTransportFactoryBuilder {
235
    @Override
236
    public ClientTransportFactory buildClientTransportFactory() {
237
      throw new UnsupportedOperationException();
×
238
    }
239
  }
240

241
  /**
242
   * An interface for Transport implementors to provide a default port to {@link
243
   * io.grpc.NameResolver} for use in cases where the target string doesn't include a port. The
244
   * default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}.
245
   */
246
  public interface ChannelBuilderDefaultPortProvider {
247
    int getDefaultPort();
248
  }
249

250
  /**
251
   * Default implementation of {@link ChannelBuilderDefaultPortProvider} that returns a fixed port.
252
   */
253
  public static final class FixedPortProvider implements ChannelBuilderDefaultPortProvider {
254
    private final int port;
255

256
    public FixedPortProvider(int port) {
1✔
257
      this.port = port;
1✔
258
    }
1✔
259

260
    @Override
261
    public int getDefaultPort() {
262
      return port;
1✔
263
    }
264
  }
265

266
  private static final class ManagedChannelDefaultPortProvider implements
267
      ChannelBuilderDefaultPortProvider {
268
    @Override
269
    public int getDefaultPort() {
270
      return GrpcUtil.DEFAULT_PORT_SSL;
1✔
271
    }
272
  }
273

274
  private final ClientTransportFactoryBuilder clientTransportFactoryBuilder;
275
  private final ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider;
276

277
  /**
278
   * Creates a new managed channel builder with a target string, which can be either a valid {@link
279
   * io.grpc.NameResolver}-compliant URI, or an authority string. Transport implementors must
280
   * provide client transport factory builder, and may set custom channel default port provider.
281
   */
282
  public ManagedChannelImplBuilder(String target,
283
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
284
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
285
    this(target, null, null, clientTransportFactoryBuilder, channelBuilderDefaultPortProvider);
1✔
286
  }
1✔
287

288
  /**
289
   * Creates a new managed channel builder with a target string, which can be either a valid {@link
290
   * io.grpc.NameResolver}-compliant URI, or an authority string. Transport implementors must
291
   * provide client transport factory builder, and may set custom channel default port provider.
292
   *
293
   * @param channelCreds The ChannelCredentials provided by the user. These may be used when
294
   *     creating derivative channels.
295
   */
296
  public ManagedChannelImplBuilder(
297
      String target, @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds,
298
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
299
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
300
    this(
1✔
301
        target,
302
        channelCreds,
303
        callCreds,
304
        clientTransportFactoryBuilder,
305
        channelBuilderDefaultPortProvider,
306
        null,
307
        null);
308
  }
1✔
309

310
  /**
311
   * Creates a new managed channel builder with a target string, which can be
312
   * either a valid {@link io.grpc.NameResolver}-compliant URI, or an authority
313
   * string. Transport
314
   * implementors must provide client transport factory builder, and may set
315
   * custom channel default
316
   * port provider.
317
   *
318
   * @param channelCreds         The ChannelCredentials provided by the user.
319
   *                             These may be used when
320
   *                             creating derivative channels.
321
   * @param nameResolverRegistry the registry used to look up name resolvers.
322
   * @param nameResolverProvider the provider used to look up name resolvers.
323
   */
324
  public ManagedChannelImplBuilder(
325
      String target, @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds,
326
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
327
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider,
328
      @Nullable NameResolverRegistry nameResolverRegistry,
329
      @Nullable NameResolverProvider nameResolverProvider) {
1✔
330
    this.target = checkNotNull(target, "target");
1✔
331
    this.channelCredentials = channelCreds;
1✔
332
    this.callCredentials = callCreds;
1✔
333
    this.clientTransportFactoryBuilder = checkNotNull(clientTransportFactoryBuilder,
1✔
334
        "clientTransportFactoryBuilder");
335
    this.directServerAddress = null;
1✔
336

337
    this.channelBuilderDefaultPortProvider =
1✔
338
        channelBuilderDefaultPortProvider != null
1✔
339
            ? channelBuilderDefaultPortProvider
1✔
340
            : new ManagedChannelDefaultPortProvider();
1✔
341
    this.nameResolverRegistry =
1✔
342
        nameResolverRegistry != null
1✔
343
            ? nameResolverRegistry
1✔
344
            : NameResolverRegistry.getDefaultRegistry();
1✔
345
    this.nameResolverProvider = nameResolverProvider;
1✔
346

347
    // TODO(dnvindhya): Move configurator to all the individual builders
348
    InternalConfiguratorRegistry.configureChannelBuilder(this);
1✔
349
  }
1✔
350

351
  /**
352
   * Returns a target string for the SocketAddress. It is only used as a placeholder, because
353
   * DirectAddressNameResolverProvider will not actually try to use it. However, it must be a valid
354
   * URI.
355
   */
356
  @VisibleForTesting
357
  static String makeTargetStringForDirectAddress(SocketAddress address) {
358
    try {
359
      return new URI(DIRECT_ADDRESS_SCHEME, "", "/" + address, null).toString();
1✔
360
    } catch (URISyntaxException e) {
×
361
      // It should not happen.
362
      throw new RuntimeException(e);
×
363
    }
364
  }
365

366
  /**
367
   * Creates a new managed channel builder with the given server address, authority string of the
368
   * channel. Transport implementors must provide client transport factory builder, and may set
369
   * custom channel default port provider.
370
   */
371
  public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
372
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
373
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
374
    this(directServerAddress, authority, null, null, clientTransportFactoryBuilder,
1✔
375
        channelBuilderDefaultPortProvider);
376
  }
1✔
377

378
  /**
379
   * Creates a new managed channel builder with the given server address, authority string of the
380
   * channel. Transport implementors must provide client transport factory builder, and may set
381
   * custom channel default port provider.
382
   * 
383
   * @param channelCreds The ChannelCredentials provided by the user. These may be used when
384
   *     creating derivative channels.
385
   */
386
  public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
387
      @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds,
388
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
389
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
1✔
390
    this.target = makeTargetStringForDirectAddress(directServerAddress);
1✔
391
    this.channelCredentials = channelCreds;
1✔
392
    this.callCredentials = callCreds;
1✔
393
    this.clientTransportFactoryBuilder = checkNotNull(clientTransportFactoryBuilder,
1✔
394
        "clientTransportFactoryBuilder");
395
    this.directServerAddress = directServerAddress;
1✔
396
    NameResolverRegistry reg = new NameResolverRegistry();
1✔
397
    reg.register(new DirectAddressNameResolverProvider(directServerAddress,
1✔
398
        authority));
399
    this.nameResolverRegistry = reg;
1✔
400

401
    if (channelBuilderDefaultPortProvider != null) {
1✔
402
      this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider;
1✔
403
    } else {
404
      this.channelBuilderDefaultPortProvider = new ManagedChannelDefaultPortProvider();
1✔
405
    }
406
    // TODO(dnvindhya): Move configurator to all the individual builders
407
    InternalConfiguratorRegistry.configureChannelBuilder(this);
1✔
408
  }
1✔
409

410
  @Override
411
  public ManagedChannelImplBuilder directExecutor() {
412
    return executor(MoreExecutors.directExecutor());
1✔
413
  }
414

415
  @Override
416
  public ManagedChannelImplBuilder executor(Executor executor) {
417
    if (executor != null) {
1✔
418
      this.executorPool = new FixedObjectPool<>(executor);
1✔
419
    } else {
420
      this.executorPool = DEFAULT_EXECUTOR_POOL;
1✔
421
    }
422
    return this;
1✔
423
  }
424

425
  @Override
426
  public ManagedChannelImplBuilder offloadExecutor(Executor executor) {
427
    if (executor != null) {
1✔
428
      this.offloadExecutorPool = new FixedObjectPool<>(executor);
1✔
429
    } else {
430
      this.offloadExecutorPool = DEFAULT_EXECUTOR_POOL;
1✔
431
    }
432
    return this;
1✔
433
  }
434

435
  @Override
436
  public ManagedChannelImplBuilder intercept(List<ClientInterceptor> interceptors) {
437
    this.interceptors.addAll(interceptors);
1✔
438
    return this;
1✔
439
  }
440

441
  @Override
442
  public ManagedChannelImplBuilder intercept(ClientInterceptor... interceptors) {
443
    return intercept(Arrays.asList(interceptors));
1✔
444
  }
445

446
  @Override
447
  protected ManagedChannelImplBuilder interceptWithTarget(InterceptorFactory factory) {
448
    // Add a placeholder instance to the interceptor list, and replace it with a real instance
449
    // during build().
450
    this.interceptors.add(new InterceptorFactoryWrapper(factory));
1✔
451
    return this;
1✔
452
  }
453

454
  @Override
455
  public ManagedChannelImplBuilder addTransportFilter(ClientTransportFilter hook) {
456
    transportFilters.add(checkNotNull(hook, "transport filter"));
1✔
457
    return this;
1✔
458
  }
459

460
  @Deprecated
461
  @Override
462
  public ManagedChannelImplBuilder nameResolverFactory(NameResolver.Factory resolverFactory) {
463
    Preconditions.checkState(directServerAddress == null,
1✔
464
        "directServerAddress is set (%s), which forbids the use of NameResolverFactory",
465
        directServerAddress);
466

467
    if (resolverFactory != null) {
1✔
468
      NameResolverRegistry reg = new NameResolverRegistry();
1✔
469
      if (resolverFactory instanceof NameResolverProvider) {
1✔
470
        reg.register((NameResolverProvider) resolverFactory);
1✔
471
      } else {
472
        reg.register(new NameResolverFactoryToProviderFacade(resolverFactory));
1✔
473
      }
474
      this.nameResolverRegistry = reg;
1✔
475
    } else {
1✔
476
      this.nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
1✔
477
    }
478
    return this;
1✔
479
  }
480

481
  ManagedChannelImplBuilder nameResolverRegistry(NameResolverRegistry resolverRegistry) {
482
    this.nameResolverRegistry = resolverRegistry;
1✔
483
    return this;
1✔
484
  }
485

486
  @Override
487
  public ManagedChannelImplBuilder defaultLoadBalancingPolicy(String policy) {
488
    Preconditions.checkState(directServerAddress == null,
1✔
489
        "directServerAddress is set (%s), which forbids the use of load-balancing policy",
490
        directServerAddress);
491
    Preconditions.checkArgument(policy != null, "policy cannot be null");
1✔
492
    this.defaultLbPolicy = policy;
1✔
493
    return this;
1✔
494
  }
495

496
  @Override
497
  public ManagedChannelImplBuilder decompressorRegistry(DecompressorRegistry registry) {
498
    if (registry != null) {
1✔
499
      this.decompressorRegistry = registry;
1✔
500
    } else {
501
      this.decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
1✔
502
    }
503
    return this;
1✔
504
  }
505

506
  @Override
507
  public ManagedChannelImplBuilder compressorRegistry(CompressorRegistry registry) {
508
    if (registry != null) {
1✔
509
      this.compressorRegistry = registry;
1✔
510
    } else {
511
      this.compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
1✔
512
    }
513
    return this;
1✔
514
  }
515

516
  @Override
517
  public ManagedChannelImplBuilder userAgent(@Nullable String userAgent) {
518
    this.userAgent = userAgent;
1✔
519
    return this;
1✔
520
  }
521

522
  @Override
523
  public ManagedChannelImplBuilder overrideAuthority(String authority) {
524
    this.authorityOverride = checkAuthority(authority);
1✔
525
    return this;
1✔
526
  }
527

528
  @Override
529
  public ManagedChannelImplBuilder idleTimeout(long value, TimeUnit unit) {
530
    checkArgument(value > 0, "idle timeout is %s, but must be positive", value);
1✔
531
    // We convert to the largest unit to avoid overflow
532
    if (unit.toDays(value) >= IDLE_MODE_MAX_TIMEOUT_DAYS) {
1✔
533
      // This disables idle mode
534
      this.idleTimeoutMillis = ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE;
1✔
535
    } else {
536
      this.idleTimeoutMillis = Math.max(unit.toMillis(value), IDLE_MODE_MIN_TIMEOUT_MILLIS);
1✔
537
    }
538
    return this;
1✔
539
  }
540

541
  @Override
542
  public ManagedChannelImplBuilder maxRetryAttempts(int maxRetryAttempts) {
543
    this.maxRetryAttempts = maxRetryAttempts;
1✔
544
    return this;
1✔
545
  }
546

547
  @Override
548
  public ManagedChannelImplBuilder maxHedgedAttempts(int maxHedgedAttempts) {
549
    this.maxHedgedAttempts = maxHedgedAttempts;
1✔
550
    return this;
1✔
551
  }
552

553
  @Override
554
  public ManagedChannelImplBuilder retryBufferSize(long bytes) {
555
    checkArgument(bytes > 0L, "retry buffer size must be positive");
1✔
556
    retryBufferSize = bytes;
1✔
557
    return this;
1✔
558
  }
559

560
  @Override
561
  public ManagedChannelImplBuilder perRpcBufferLimit(long bytes) {
562
    checkArgument(bytes > 0L, "per RPC buffer limit must be positive");
1✔
563
    perRpcBufferLimit = bytes;
1✔
564
    return this;
1✔
565
  }
566

567
  @Override
568
  public ManagedChannelImplBuilder disableRetry() {
569
    retryEnabled = false;
1✔
570
    return this;
1✔
571
  }
572

573
  @Override
574
  public ManagedChannelImplBuilder enableRetry() {
575
    retryEnabled = true;
1✔
576
    return this;
1✔
577
  }
578

579
  @Override
580
  public ManagedChannelImplBuilder setBinaryLog(BinaryLog binlog) {
581
    this.binlog = binlog;
×
582
    return this;
×
583
  }
584

585
  @Override
586
  public ManagedChannelImplBuilder maxTraceEvents(int maxTraceEvents) {
587
    checkArgument(maxTraceEvents >= 0, "maxTraceEvents must be non-negative");
1✔
588
    this.maxTraceEvents = maxTraceEvents;
1✔
589
    return this;
1✔
590
  }
591

592
  @Override
593
  public ManagedChannelImplBuilder proxyDetector(@Nullable ProxyDetector proxyDetector) {
594
    this.proxyDetector = proxyDetector;
1✔
595
    return this;
1✔
596
  }
597

598
  @Override
599
  public ManagedChannelImplBuilder defaultServiceConfig(@Nullable Map<String, ?> serviceConfig) {
600
    // TODO(notcarl): use real parsing
601
    defaultServiceConfig = checkMapEntryTypes(serviceConfig);
1✔
602
    return this;
1✔
603
  }
604

605
  @Nullable
606
  private static Map<String, ?> checkMapEntryTypes(@Nullable Map<?, ?> map) {
607
    if (map == null) {
1✔
608
      return null;
×
609
    }
610
    // Not using ImmutableMap.Builder because of extra guava dependency for Android.
611
    Map<String, Object> parsedMap = new LinkedHashMap<>();
1✔
612
    for (Map.Entry<?, ?> entry : map.entrySet()) {
1✔
613
      checkArgument(
1✔
614
          entry.getKey() instanceof String,
1✔
615
          "The key of the entry '%s' is not of String type", entry);
616

617
      String key = (String) entry.getKey();
1✔
618
      Object value = entry.getValue();
1✔
619
      if (value == null) {
1✔
620
        parsedMap.put(key, null);
1✔
621
      } else if (value instanceof Map) {
1✔
622
        parsedMap.put(key, checkMapEntryTypes((Map<?, ?>) value));
1✔
623
      } else if (value instanceof List) {
1✔
624
        parsedMap.put(key, checkListEntryTypes((List<?>) value));
1✔
625
      } else if (value instanceof String) {
1✔
626
        parsedMap.put(key, value);
1✔
627
      } else if (value instanceof Number) {
1✔
628
        parsedMap.put(key, ((Number) value).doubleValue());
1✔
629
      } else if (value instanceof Boolean) {
1✔
630
        parsedMap.put(key, value);
1✔
631
      } else {
632
        throw new IllegalArgumentException(
×
633
            "The value of the map entry '" + entry + "' is of type '" + value.getClass()
×
634
                + "', which is not supported");
635
      }
636
    }
1✔
637
    return Collections.unmodifiableMap(parsedMap);
1✔
638
  }
639

640
  private static List<?> checkListEntryTypes(List<?> list) {
641
    List<Object> parsedList = new ArrayList<>(list.size());
1✔
642
    for (Object value : list) {
1✔
643
      if (value == null) {
1✔
644
        parsedList.add(null);
1✔
645
      } else if (value instanceof Map) {
1✔
646
        parsedList.add(checkMapEntryTypes((Map<?, ?>) value));
1✔
647
      } else if (value instanceof List) {
1✔
648
        parsedList.add(checkListEntryTypes((List<?>) value));
×
649
      } else if (value instanceof String) {
1✔
650
        parsedList.add(value);
1✔
651
      } else if (value instanceof Number) {
1✔
652
        parsedList.add(((Number) value).doubleValue());
1✔
653
      } else if (value instanceof Boolean) {
1✔
654
        parsedList.add(value);
1✔
655
      } else {
656
        throw new IllegalArgumentException(
×
657
            "The entry '" + value + "' is of type '" + value.getClass()
×
658
                + "', which is not supported");
659
      }
660
    }
1✔
661
    return Collections.unmodifiableList(parsedList);
1✔
662
  }
663

664
  @Override
665
  public <X> ManagedChannelImplBuilder setNameResolverArg(NameResolver.Args.Key<X> key, X value) {
666
    if (nameResolverCustomArgs == null) {
1✔
667
      nameResolverCustomArgs = new IdentityHashMap<>();
1✔
668
    }
669
    nameResolverCustomArgs.put(checkNotNull(key, "key"), checkNotNull(value, "value"));
1✔
670
    return this;
1✔
671
  }
672

673
  @SuppressWarnings("unchecked") // This cast is safe because of setNameResolverArg()'s signature.
674
  void copyAllNameResolverCustomArgsTo(NameResolver.Args.Builder dest) {
675
    if (nameResolverCustomArgs != null) {
1✔
676
      for (Map.Entry<NameResolver.Args.Key<?>, Object> entry : nameResolverCustomArgs.entrySet()) {
1✔
677
        dest.setArg((NameResolver.Args.Key<Object>) entry.getKey(), entry.getValue());
1✔
678
      }
1✔
679
    }
680
  }
1✔
681

682
  @Override
683
  public ManagedChannelImplBuilder disableServiceConfigLookUp() {
684
    this.lookUpServiceConfig = false;
1✔
685
    return this;
1✔
686
  }
687

688
  /**
689
   * Disable or enable stats features. Enabled by default.
690
   *
691
   * <p>For the current release, calling {@code setStatsEnabled(true)} may have a side effect that
692
   * disables retry.
693
   */
694
  public void setStatsEnabled(boolean value) {
695
    statsEnabled = value;
1✔
696
  }
1✔
697

698
  /**
699
   * Disable or enable stats recording for RPC upstarts.  Effective only if {@link
700
   * #setStatsEnabled} is set to true.  Enabled by default.
701
   */
702
  public void setStatsRecordStartedRpcs(boolean value) {
703
    recordStartedRpcs = value;
1✔
704
  }
1✔
705

706
  /**
707
   * Disable or enable stats recording for RPC completions.  Effective only if {@link
708
   * #setStatsEnabled} is set to true.  Enabled by default.
709
   */
710
  public void setStatsRecordFinishedRpcs(boolean value) {
711
    recordFinishedRpcs = value;
1✔
712
  }
1✔
713

714
  /**
715
   * Disable or enable real-time metrics recording.  Effective only if {@link #setStatsEnabled} is
716
   * set to true.  Disabled by default.
717
   */
718
  public void setStatsRecordRealTimeMetrics(boolean value) {
719
    recordRealTimeMetrics = value;
×
720
  }
×
721
  
722
  public void setStatsRecordRetryMetrics(boolean value) {
723
    recordRetryMetrics = value;
1✔
724
  }
1✔
725

726
  /**
727
   * Disable or enable tracing features.  Enabled by default.
728
   */
729
  public void setTracingEnabled(boolean value) {
730
    tracingEnabled = value;
1✔
731
  }
1✔
732

733
  /**
734
   * Verifies the authority is valid.
735
   */
736
  @VisibleForTesting
737
  String checkAuthority(String authority) {
738
    if (authorityCheckerDisabled) {
1✔
739
      return authority;
1✔
740
    }
741
    return GrpcUtil.checkAuthority(authority);
1✔
742
  }
743

744
  /** Disable the check whether the authority is valid. */
745
  public ManagedChannelImplBuilder disableCheckAuthority() {
746
    authorityCheckerDisabled = true;
1✔
747
    return this;
1✔
748
  }
749

750
  /** Enable previously disabled authority check. */
751
  public ManagedChannelImplBuilder enableCheckAuthority() {
752
    authorityCheckerDisabled = false;
1✔
753
    return this;
1✔
754
  }
755

756
  @Override
757
  protected ManagedChannelImplBuilder addMetricSink(MetricSink metricSink) {
758
    metricSinks.add(checkNotNull(metricSink, "metric sink"));
1✔
759
    return this;
1✔
760
  }
761

762
  @Override
763
  public ManagedChannelImplBuilder childChannelConfigurator(
764
      ChannelConfigurator channelConfigurator) {
765
    this.channelConfigurator = checkNotNull(channelConfigurator,
1✔
766
        "childChannelConfigurator");
767
    return this;
1✔
768
  }
769

770
  @Override
771
  public ManagedChannel build() {
772
    ClientTransportFactory clientTransportFactory =
1✔
773
        clientTransportFactoryBuilder.buildClientTransportFactory();
1✔
774
    ResolvedNameResolver resolvedResolver =
775
        InternalFeatureFlags.getRfc3986UrisEnabled()
1✔
776
            ? getNameResolverProviderRfc3986(target, nameResolverRegistry, nameResolverProvider)
1✔
777
            : getNameResolverProvider(target, nameResolverRegistry, nameResolverProvider);
1✔
778
    resolvedResolver.checkAddressTypes(clientTransportFactory.getSupportedSocketAddressTypes());
1✔
779
    return new ManagedChannelOrphanWrapper(new ManagedChannelImpl(
1✔
780
        this,
781
        clientTransportFactory,
782
        resolvedResolver.targetUri,
783
        resolvedResolver.provider,
784
        new ExponentialBackoffPolicy.Provider(),
785
        SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR),
1✔
786
        GrpcUtil.STOPWATCH_SUPPLIER,
787
        getEffectiveInterceptors(resolvedResolver.targetUri.toString()),
1✔
788
        TimeProvider.SYSTEM_TIME_PROVIDER));
789
  }
790

791
  // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know
792
  // what should be the desired behavior for retry + stats/tracing.
793
  // TODO(zdapeng): FIX IT
794
  @VisibleForTesting
795
  List<ClientInterceptor> getEffectiveInterceptors(String computedTarget) {
796
    List<ClientInterceptor> effectiveInterceptors = new ArrayList<>(this.interceptors.size());
1✔
797
    for (ClientInterceptor interceptor : this.interceptors) {
1✔
798
      if (interceptor instanceof InterceptorFactoryWrapper) {
1✔
799
        InterceptorFactory factory = ((InterceptorFactoryWrapper) interceptor).factory;
1✔
800
        interceptor = factory.newInterceptor(computedTarget);
1✔
801
        if (interceptor == null) {
1✔
802
          throw new NullPointerException("Factory returned null interceptor: " + factory);
×
803
        }
804
      }
805
      effectiveInterceptors.add(interceptor);
1✔
806
    }
1✔
807

808
    boolean disableImplicitCensus = InternalConfiguratorRegistry.wasSetConfiguratorsCalled();
1✔
809
    if (disableImplicitCensus) {
1✔
810
      return effectiveInterceptors;
×
811
    }
812
    if (statsEnabled) {
1✔
813
      ClientInterceptor statsInterceptor = null;
1✔
814

815
      if (GET_CLIENT_INTERCEPTOR_METHOD != null) {
1✔
816
        try {
817
          statsInterceptor =
1✔
818
              (ClientInterceptor) GET_CLIENT_INTERCEPTOR_METHOD
819
              .invoke(
1✔
820
                null,
821
                recordStartedRpcs,
1✔
822
                recordFinishedRpcs,
1✔
823
                recordRealTimeMetrics,
1✔
824
                recordRetryMetrics);
1✔
825
        } catch (IllegalAccessException e) {
×
826
          log.log(Level.FINE, "Unable to apply census stats", e);
×
827
        } catch (InvocationTargetException e) {
×
828
          log.log(Level.FINE, "Unable to apply census stats", e);
×
829
        }
1✔
830
      }
831

832
      if (statsInterceptor != null) {
1✔
833
        // First interceptor runs last (see ClientInterceptors.intercept()), so that no
834
        // other interceptor can override the tracer factory we set in CallOptions.
835
        effectiveInterceptors.add(0, statsInterceptor);
1✔
836
      }
837
    }
838
    if (tracingEnabled) {
1✔
839
      ClientInterceptor tracingInterceptor = null;
1✔
840
      try {
841
        Class<?> censusTracingAccessor =
1✔
842
            Class.forName("io.grpc.census.InternalCensusTracingAccessor");
1✔
843
        Method getClientInterceptroMethod =
1✔
844
            censusTracingAccessor.getDeclaredMethod("getClientInterceptor");
1✔
845
        tracingInterceptor = (ClientInterceptor) getClientInterceptroMethod.invoke(null);
1✔
846
      } catch (ClassNotFoundException e) {
1✔
847
        // Replace these separate catch statements with multicatch when Android min-API >= 19
848
        log.log(Level.FINE, "Unable to apply census stats", e);
1✔
849
      } catch (NoSuchMethodException e) {
×
850
        log.log(Level.FINE, "Unable to apply census stats", e);
×
851
      } catch (IllegalAccessException e) {
×
852
        log.log(Level.FINE, "Unable to apply census stats", e);
×
853
      } catch (InvocationTargetException e) {
×
854
        log.log(Level.FINE, "Unable to apply census stats", e);
×
855
      }
1✔
856
      if (tracingInterceptor != null) {
1✔
857
        effectiveInterceptors.add(0, tracingInterceptor);
1✔
858
      }
859
    }
860
    return effectiveInterceptors;
1✔
861
  }
862

863
  /**
864
   * Returns a default port to {@link NameResolver} for use in cases where the target string doesn't
865
   * include a port. The default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}.
866
   */
867
  int getDefaultPort() {
868
    return channelBuilderDefaultPortProvider.getDefaultPort();
1✔
869
  }
870

871
  @VisibleForTesting
872
  static class ResolvedNameResolver {
873
    public final UriWrapper targetUri;
874
    public final NameResolverProvider provider;
875

876
    public ResolvedNameResolver(UriWrapper targetUri, NameResolverProvider provider) {
1✔
877
      this.targetUri = checkNotNull(targetUri, "targetUri");
1✔
878
      this.provider = checkNotNull(provider, "provider");
1✔
879
    }
1✔
880

881
    void checkAddressTypes(
882
        Collection<Class<? extends SocketAddress>> channelTransportSocketAddressTypes) {
883
      if (channelTransportSocketAddressTypes != null) {
1✔
884
        Collection<Class<? extends SocketAddress>> nameResolverSocketAddressTypes =
1✔
885
            provider.getProducedSocketAddressTypes();
1✔
886
        if (!channelTransportSocketAddressTypes.containsAll(nameResolverSocketAddressTypes)) {
1✔
887
          throw new IllegalArgumentException(
1✔
888
              String.format(
1✔
889
                  "Address types of NameResolver '%s' for '%s' not supported by transport",
890
                  provider.getDefaultScheme(), targetUri));
1✔
891
        }
892
      }
893
    }
1✔
894
  }
895

896
  @VisibleForTesting
897
  static ResolvedNameResolver getNameResolverProvider(
898
      String target, NameResolverRegistry nameResolverRegistry,
899
      NameResolverProvider nameResolverProvider) {
900
    // Finding a NameResolver. Try using the target string as the URI. If that fails, try prepending
901
    // "dns:///".
902
    NameResolverProvider provider = null;
1✔
903
    URI targetUri = null;
1✔
904
    StringBuilder uriSyntaxErrors = new StringBuilder();
1✔
905
    try {
906
      targetUri = new URI(target);
1✔
907
    } catch (URISyntaxException e) {
1✔
908
      // Can happen with ip addresses like "[::1]:1234" or 127.0.0.1:1234.
909
      uriSyntaxErrors.append(e.getMessage());
1✔
910
    }
1✔
911
    if (targetUri != null) {
1✔
912
      // For "localhost:8080" this would likely cause provider to be null, because "localhost" is
913
      // parsed as the scheme. Will hit the next case and try "dns:///localhost:8080".
914
      // Use the explicit provider if its scheme matches the target URI.
915
      if (nameResolverProvider != null
1✔
916
          && targetUri.getScheme().equals(nameResolverProvider.getScheme())) {
1✔
917
        provider = nameResolverProvider;
1✔
918
      } else {
919
        provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
920
      }
921
    }
922

923
    if (provider == null && !URI_PATTERN.matcher(target).matches()) {
1✔
924
      // It doesn't look like a URI target. Maybe it's an authority string. Try with
925
      // the default scheme from the registry (if provider is not specified) or
926
      // the provider's default scheme (if provider is specified).
927
      String scheme = nameResolverProvider != null
1✔
928
          ? nameResolverProvider.getScheme()
1✔
929
          : nameResolverRegistry.getDefaultScheme();
1✔
930
      try {
931
        targetUri = new URI(scheme, "", "/" + target, null);
1✔
932
      } catch (URISyntaxException e) {
×
933
        // Should not be possible
934
        throw new IllegalArgumentException(e);
×
935
      }
1✔
936
      if (nameResolverProvider != null) {
1✔
937
        provider = nameResolverProvider;
1✔
938
      } else {
939
        provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
940
      }
941
    }
942

943
    if (provider == null) {
1✔
944
      throw new IllegalArgumentException(String.format(
1✔
945
          "Could not find a NameResolverProvider for %s%s",
946
          target, uriSyntaxErrors.length() > 0 ? " (" + uriSyntaxErrors + ")" : ""));
1✔
947
    }
948

949
    return new ResolvedNameResolver(wrap(targetUri), provider);
1✔
950
  }
951

952
  @VisibleForTesting
953
  static ResolvedNameResolver getNameResolverProviderRfc3986(
954
      String target, NameResolverRegistry nameResolverRegistry,
955
      NameResolverProvider nameResolverProvider) {
956
    // Finding a NameResolver. Try using the target string as the URI. If that fails, try prepending
957
    // "dns:///".
958
    NameResolverProvider provider = null;
1✔
959
    Uri targetUri = null;
1✔
960
    StringBuilder uriSyntaxErrors = new StringBuilder();
1✔
961
    try {
962
      targetUri = Uri.parse(target);
1✔
963
    } catch (URISyntaxException e) {
1✔
964
      // Can happen with ip addresses like "[::1]:1234" or 127.0.0.1:1234.
965
      uriSyntaxErrors.append(e.getMessage());
1✔
966
    }
1✔
967
    if (targetUri != null) {
1✔
968
      // For "localhost:8080" this would likely cause provider to be null, because "localhost" is
969
      // parsed as the scheme. Will hit the next case and try "dns:///localhost:8080".
970
      // Use the explicit provider if its scheme matches the target URI.
971
      if (nameResolverProvider != null
1✔
972
          && targetUri.getScheme().equals(nameResolverProvider.getScheme())) {
1✔
973
        provider = nameResolverProvider;
1✔
974
      } else {
975
        provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
976
      }
977
    }
978

979
    if (provider == null && !URI_PATTERN.matcher(target).matches()) {
1✔
980
      // It doesn't look like a URI target. Maybe it's an authority string. Try with
981
      // the default scheme from the registry (if provider is not specified) or
982
      // the provider's default scheme (if provider is specified).
983
      String scheme = nameResolverProvider != null
1✔
984
          ? nameResolverProvider.getScheme()
1✔
985
          : nameResolverRegistry.getDefaultScheme();
1✔
986
      targetUri =
987
          Uri.newBuilder()
1✔
988
              .setScheme(scheme)
1✔
989
              .setHost("")
1✔
990
              .setPath("/" + target)
1✔
991
              .build();
1✔
992
      provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
993
    }
994

995
    if (provider == null) {
1✔
996
      throw new IllegalArgumentException(
1✔
997
          String.format(
1✔
998
              "Could not find a NameResolverProvider for %s%s",
999
              target, uriSyntaxErrors.length() > 0 ? " (" + uriSyntaxErrors + ")" : ""));
1✔
1000
    }
1001

1002
    return new ResolvedNameResolver(wrap(targetUri), provider);
1✔
1003
  }
1004

1005
  private static class DirectAddressNameResolverProvider extends NameResolverProvider {
1006
    final SocketAddress address;
1007
    final String authority;
1008
    final Collection<Class<? extends SocketAddress>> producedSocketAddressTypes;
1009

1010
    DirectAddressNameResolverProvider(SocketAddress address, String authority) {
1✔
1011
      this.address = address;
1✔
1012
      this.authority = authority;
1✔
1013
      this.producedSocketAddressTypes
1✔
1014
          = Collections.singleton(address.getClass());
1✔
1015
    }
1✔
1016

1017
    @Override
1018
    public NameResolver newNameResolver(URI notUsedUri, NameResolver.Args args) {
1019
      return new NameResolver() {
1✔
1020
        @Override
1021
        public String getServiceAuthority() {
1022
          return authority;
1✔
1023
        }
1024

1025
        @Override
1026
        public void start(Listener2 listener) {
1027
          listener.onResult2(
1✔
1028
              ResolutionResult.newBuilder()
1✔
1029
                  .setAddressesOrError(
1✔
1030
                      StatusOr.fromValue(
1✔
1031
                          Collections.singletonList(new EquivalentAddressGroup(address))))
1✔
1032
                  .setAttributes(Attributes.EMPTY)
1✔
1033
                  .build());
1✔
1034
        }
1✔
1035

1036
        @Override
1037
        public void shutdown() {}
1✔
1038
      };
1039
    }
1040

1041
    @Override
1042
    public String getDefaultScheme() {
1043
      return DIRECT_ADDRESS_SCHEME;
1✔
1044
    }
1045

1046
    @Override
1047
    protected boolean isAvailable() {
1048
      return true;
1✔
1049
    }
1050

1051
    @Override
1052
    protected int priority() {
1053
      return 5;
1✔
1054
    }
1055

1056
    @Override
1057
    public Collection<Class<? extends SocketAddress>> getProducedSocketAddressTypes() {
1058
      return producedSocketAddressTypes;
1✔
1059
    }
1060
  }
1061

1062
  private static final class InterceptorFactoryWrapper implements ClientInterceptor {
1063
    final InterceptorFactory factory;
1064

1065
    public InterceptorFactoryWrapper(InterceptorFactory factory) {
1✔
1066
      this.factory = checkNotNull(factory, "factory");
1✔
1067
    }
1✔
1068

1069
    @Override
1070
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
1071
        MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
1072
      throw new AssertionError("Should have been replaced with real instance");
×
1073
    }
1074
  }
1075

1076
  /**
1077
   * Returns the internal offload executor pool for offloading tasks.
1078
   */
1079
  public ObjectPool<? extends Executor> getOffloadExecutorPool() {
1080
    return this.offloadExecutorPool;
1✔
1081
  }
1082
}
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