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

grpc / grpc-java / #20139

06 Jan 2026 07:35PM UTC coverage: 88.696% (-0.006%) from 88.702%
#20139

push

github

jdcormie
binder: Migrate IntentNameResolverProvider to io.grpc.Uri

35425 of 39940 relevant lines covered (88.7%)

0.89 hits per line

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

91.87
/../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.ChannelCredentials;
33
import io.grpc.ClientCall;
34
import io.grpc.ClientInterceptor;
35
import io.grpc.ClientTransportFilter;
36
import io.grpc.CompressorRegistry;
37
import io.grpc.DecompressorRegistry;
38
import io.grpc.EquivalentAddressGroup;
39
import io.grpc.InternalChannelz;
40
import io.grpc.InternalConfiguratorRegistry;
41
import io.grpc.ManagedChannel;
42
import io.grpc.ManagedChannelBuilder;
43
import io.grpc.MethodDescriptor;
44
import io.grpc.MetricSink;
45
import io.grpc.NameResolver;
46
import io.grpc.NameResolverProvider;
47
import io.grpc.NameResolverRegistry;
48
import io.grpc.ProxyDetector;
49
import io.grpc.StatusOr;
50
import io.grpc.Uri;
51
import java.lang.reflect.InvocationTargetException;
52
import java.lang.reflect.Method;
53
import java.net.SocketAddress;
54
import java.net.URI;
55
import java.net.URISyntaxException;
56
import java.util.ArrayList;
57
import java.util.Arrays;
58
import java.util.Collection;
59
import java.util.Collections;
60
import java.util.IdentityHashMap;
61
import java.util.LinkedHashMap;
62
import java.util.List;
63
import java.util.Map;
64
import java.util.concurrent.Executor;
65
import java.util.concurrent.TimeUnit;
66
import java.util.logging.Level;
67
import java.util.logging.Logger;
68
import java.util.regex.Pattern;
69
import javax.annotation.Nullable;
70

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

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

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

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

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

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

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

109
  private static boolean enableRfc3986Uris = GrpcUtil.getFlag("GRPC_ENABLE_RFC3986_URIS", false);
1✔
110

111
  /** Whether to parse targets as RFC 3986 URIs (true), or use {@link java.net.URI} (false). */
112
  @VisibleForTesting
113
  static boolean setRfc3986UrisEnabled(boolean value) {
114
    boolean prevValue = ManagedChannelImplBuilder.enableRfc3986Uris;
1✔
115
    ManagedChannelImplBuilder.enableRfc3986Uris = value;
1✔
116
    return prevValue;
1✔
117
  }
118

119
  private static final ObjectPool<? extends Executor> DEFAULT_EXECUTOR_POOL =
1✔
120
      SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR);
1✔
121

122
  private static final DecompressorRegistry DEFAULT_DECOMPRESSOR_REGISTRY =
123
      DecompressorRegistry.getDefaultInstance();
1✔
124

125
  private static final CompressorRegistry DEFAULT_COMPRESSOR_REGISTRY =
126
      CompressorRegistry.getDefaultInstance();
1✔
127

128
  private static final long DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES = 1L << 24;  // 16M
129
  private static final long DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES = 1L << 20; // 1M
130

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

137
  private static final Method GET_CLIENT_INTERCEPTOR_METHOD;
138

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

160

161
  ObjectPool<? extends Executor> executorPool = DEFAULT_EXECUTOR_POOL;
1✔
162

163
  ObjectPool<? extends Executor> offloadExecutorPool = DEFAULT_EXECUTOR_POOL;
1✔
164

165
  private final List<ClientInterceptor> interceptors = new ArrayList<>();
1✔
166
  NameResolverRegistry nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
1✔
167

168
  final List<ClientTransportFilter> transportFilters = new ArrayList<>();
1✔
169

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

178
  @Nullable
179
  private final SocketAddress directServerAddress;
180

181
  @Nullable
182
  String userAgent;
183

184
  @Nullable
185
  String authorityOverride;
186

187
  String defaultLbPolicy = GrpcUtil.DEFAULT_LB_POLICY;
1✔
188

189
  boolean fullStreamDecompression;
190

191
  DecompressorRegistry decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
1✔
192

193
  CompressorRegistry compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
1✔
194

195
  long idleTimeoutMillis = IDLE_MODE_DEFAULT_TIMEOUT_MILLIS;
1✔
196

197
  int maxRetryAttempts = 5;
1✔
198
  int maxHedgedAttempts = 5;
1✔
199
  long retryBufferSize = DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES;
1✔
200
  long perRpcBufferLimit = DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES;
1✔
201
  boolean retryEnabled = true;
1✔
202

203
  InternalChannelz channelz = InternalChannelz.instance();
1✔
204
  int maxTraceEvents;
205

206
  @Nullable
207
  Map<String, ?> defaultServiceConfig;
208
  boolean lookUpServiceConfig = true;
1✔
209

210
  @Nullable
211
  BinaryLog binlog;
212

213
  @Nullable
214
  ProxyDetector proxyDetector;
215

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

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

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

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

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

259
    public FixedPortProvider(int port) {
1✔
260
      this.port = port;
1✔
261
    }
1✔
262

263
    @Override
264
    public int getDefaultPort() {
265
      return port;
1✔
266
    }
267
  }
268

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

277
  private final ClientTransportFactoryBuilder clientTransportFactoryBuilder;
278
  private final ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider;
279

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

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

310
    if (channelBuilderDefaultPortProvider != null) {
1✔
311
      this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider;
1✔
312
    } else {
313
      this.channelBuilderDefaultPortProvider = new ManagedChannelDefaultPortProvider();
1✔
314
    }
315
    // TODO(dnvindhya): Move configurator to all the individual builders
316
    InternalConfiguratorRegistry.configureChannelBuilder(this);
1✔
317
  }
1✔
318

319
  /**
320
   * Returns a target string for the SocketAddress. It is only used as a placeholder, because
321
   * DirectAddressNameResolverProvider will not actually try to use it. However, it must be a valid
322
   * URI.
323
   */
324
  @VisibleForTesting
325
  static String makeTargetStringForDirectAddress(SocketAddress address) {
326
    try {
327
      return new URI(DIRECT_ADDRESS_SCHEME, "", "/" + address, null).toString();
1✔
328
    } catch (URISyntaxException e) {
×
329
      // It should not happen.
330
      throw new RuntimeException(e);
×
331
    }
332
  }
333

334
  /**
335
   * Creates a new managed channel builder with the given server address, authority string of the
336
   * channel. Transport implementors must provide client transport factory builder, and may set
337
   * custom channel default port provider.
338
   */
339
  public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
340
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
341
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
342
    this(directServerAddress, authority, null, null, clientTransportFactoryBuilder,
1✔
343
        channelBuilderDefaultPortProvider);
344
  }
1✔
345

346
  /**
347
   * Creates a new managed channel builder with the given server address, authority string of the
348
   * channel. Transport implementors must provide client transport factory builder, and may set
349
   * custom channel default port provider.
350
   * 
351
   * @param channelCreds The ChannelCredentials provided by the user. These may be used when
352
   *     creating derivative channels.
353
   */
354
  public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
355
      @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds,
356
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
357
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
1✔
358
    this.target = makeTargetStringForDirectAddress(directServerAddress);
1✔
359
    this.channelCredentials = channelCreds;
1✔
360
    this.callCredentials = callCreds;
1✔
361
    this.clientTransportFactoryBuilder = checkNotNull(clientTransportFactoryBuilder,
1✔
362
        "clientTransportFactoryBuilder");
363
    this.directServerAddress = directServerAddress;
1✔
364
    NameResolverRegistry reg = new NameResolverRegistry();
1✔
365
    reg.register(new DirectAddressNameResolverProvider(directServerAddress,
1✔
366
        authority));
367
    this.nameResolverRegistry = reg;
1✔
368

369
    if (channelBuilderDefaultPortProvider != null) {
1✔
370
      this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider;
1✔
371
    } else {
372
      this.channelBuilderDefaultPortProvider = new ManagedChannelDefaultPortProvider();
1✔
373
    }
374
    // TODO(dnvindhya): Move configurator to all the individual builders
375
    InternalConfiguratorRegistry.configureChannelBuilder(this);
1✔
376
  }
1✔
377

378
  @Override
379
  public ManagedChannelImplBuilder directExecutor() {
380
    return executor(MoreExecutors.directExecutor());
1✔
381
  }
382

383
  @Override
384
  public ManagedChannelImplBuilder executor(Executor executor) {
385
    if (executor != null) {
1✔
386
      this.executorPool = new FixedObjectPool<>(executor);
1✔
387
    } else {
388
      this.executorPool = DEFAULT_EXECUTOR_POOL;
1✔
389
    }
390
    return this;
1✔
391
  }
392

393
  @Override
394
  public ManagedChannelImplBuilder offloadExecutor(Executor executor) {
395
    if (executor != null) {
1✔
396
      this.offloadExecutorPool = new FixedObjectPool<>(executor);
1✔
397
    } else {
398
      this.offloadExecutorPool = DEFAULT_EXECUTOR_POOL;
1✔
399
    }
400
    return this;
1✔
401
  }
402

403
  @Override
404
  public ManagedChannelImplBuilder intercept(List<ClientInterceptor> interceptors) {
405
    this.interceptors.addAll(interceptors);
1✔
406
    return this;
1✔
407
  }
408

409
  @Override
410
  public ManagedChannelImplBuilder intercept(ClientInterceptor... interceptors) {
411
    return intercept(Arrays.asList(interceptors));
1✔
412
  }
413

414
  @Override
415
  protected ManagedChannelImplBuilder interceptWithTarget(InterceptorFactory factory) {
416
    // Add a placeholder instance to the interceptor list, and replace it with a real instance
417
    // during build().
418
    this.interceptors.add(new InterceptorFactoryWrapper(factory));
1✔
419
    return this;
1✔
420
  }
421

422
  @Override
423
  public ManagedChannelImplBuilder addTransportFilter(ClientTransportFilter hook) {
424
    transportFilters.add(checkNotNull(hook, "transport filter"));
1✔
425
    return this;
1✔
426
  }
427

428
  @Deprecated
429
  @Override
430
  public ManagedChannelImplBuilder nameResolverFactory(NameResolver.Factory resolverFactory) {
431
    Preconditions.checkState(directServerAddress == null,
1✔
432
        "directServerAddress is set (%s), which forbids the use of NameResolverFactory",
433
        directServerAddress);
434
    if (resolverFactory != null) {
1✔
435
      NameResolverRegistry reg = new NameResolverRegistry();
1✔
436
      if (resolverFactory instanceof NameResolverProvider) {
1✔
437
        reg.register((NameResolverProvider) resolverFactory);
1✔
438
      } else {
439
        reg.register(new NameResolverFactoryToProviderFacade(resolverFactory));
1✔
440
      }
441
      this.nameResolverRegistry = reg;
1✔
442
    } else {
1✔
443
      this.nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
1✔
444
    }
445
    return this;
1✔
446
  }
447

448
  ManagedChannelImplBuilder nameResolverRegistry(NameResolverRegistry resolverRegistry) {
449
    this.nameResolverRegistry = resolverRegistry;
1✔
450
    return this;
1✔
451
  }
452

453
  @Override
454
  public ManagedChannelImplBuilder defaultLoadBalancingPolicy(String policy) {
455
    Preconditions.checkState(directServerAddress == null,
1✔
456
        "directServerAddress is set (%s), which forbids the use of load-balancing policy",
457
        directServerAddress);
458
    Preconditions.checkArgument(policy != null, "policy cannot be null");
1✔
459
    this.defaultLbPolicy = policy;
1✔
460
    return this;
1✔
461
  }
462

463
  @Override
464
  public ManagedChannelImplBuilder decompressorRegistry(DecompressorRegistry registry) {
465
    if (registry != null) {
1✔
466
      this.decompressorRegistry = registry;
1✔
467
    } else {
468
      this.decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
1✔
469
    }
470
    return this;
1✔
471
  }
472

473
  @Override
474
  public ManagedChannelImplBuilder compressorRegistry(CompressorRegistry registry) {
475
    if (registry != null) {
1✔
476
      this.compressorRegistry = registry;
1✔
477
    } else {
478
      this.compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
1✔
479
    }
480
    return this;
1✔
481
  }
482

483
  @Override
484
  public ManagedChannelImplBuilder userAgent(@Nullable String userAgent) {
485
    this.userAgent = userAgent;
1✔
486
    return this;
1✔
487
  }
488

489
  @Override
490
  public ManagedChannelImplBuilder overrideAuthority(String authority) {
491
    this.authorityOverride = checkAuthority(authority);
1✔
492
    return this;
1✔
493
  }
494

495
  @Override
496
  public ManagedChannelImplBuilder idleTimeout(long value, TimeUnit unit) {
497
    checkArgument(value > 0, "idle timeout is %s, but must be positive", value);
1✔
498
    // We convert to the largest unit to avoid overflow
499
    if (unit.toDays(value) >= IDLE_MODE_MAX_TIMEOUT_DAYS) {
1✔
500
      // This disables idle mode
501
      this.idleTimeoutMillis = ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE;
1✔
502
    } else {
503
      this.idleTimeoutMillis = Math.max(unit.toMillis(value), IDLE_MODE_MIN_TIMEOUT_MILLIS);
1✔
504
    }
505
    return this;
1✔
506
  }
507

508
  @Override
509
  public ManagedChannelImplBuilder maxRetryAttempts(int maxRetryAttempts) {
510
    this.maxRetryAttempts = maxRetryAttempts;
1✔
511
    return this;
1✔
512
  }
513

514
  @Override
515
  public ManagedChannelImplBuilder maxHedgedAttempts(int maxHedgedAttempts) {
516
    this.maxHedgedAttempts = maxHedgedAttempts;
1✔
517
    return this;
1✔
518
  }
519

520
  @Override
521
  public ManagedChannelImplBuilder retryBufferSize(long bytes) {
522
    checkArgument(bytes > 0L, "retry buffer size must be positive");
1✔
523
    retryBufferSize = bytes;
1✔
524
    return this;
1✔
525
  }
526

527
  @Override
528
  public ManagedChannelImplBuilder perRpcBufferLimit(long bytes) {
529
    checkArgument(bytes > 0L, "per RPC buffer limit must be positive");
1✔
530
    perRpcBufferLimit = bytes;
1✔
531
    return this;
1✔
532
  }
533

534
  @Override
535
  public ManagedChannelImplBuilder disableRetry() {
536
    retryEnabled = false;
1✔
537
    return this;
1✔
538
  }
539

540
  @Override
541
  public ManagedChannelImplBuilder enableRetry() {
542
    retryEnabled = true;
1✔
543
    return this;
1✔
544
  }
545

546
  @Override
547
  public ManagedChannelImplBuilder setBinaryLog(BinaryLog binlog) {
548
    this.binlog = binlog;
×
549
    return this;
×
550
  }
551

552
  @Override
553
  public ManagedChannelImplBuilder maxTraceEvents(int maxTraceEvents) {
554
    checkArgument(maxTraceEvents >= 0, "maxTraceEvents must be non-negative");
1✔
555
    this.maxTraceEvents = maxTraceEvents;
1✔
556
    return this;
1✔
557
  }
558

559
  @Override
560
  public ManagedChannelImplBuilder proxyDetector(@Nullable ProxyDetector proxyDetector) {
561
    this.proxyDetector = proxyDetector;
1✔
562
    return this;
1✔
563
  }
564

565
  @Override
566
  public ManagedChannelImplBuilder defaultServiceConfig(@Nullable Map<String, ?> serviceConfig) {
567
    // TODO(notcarl): use real parsing
568
    defaultServiceConfig = checkMapEntryTypes(serviceConfig);
1✔
569
    return this;
1✔
570
  }
571

572
  @Nullable
573
  private static Map<String, ?> checkMapEntryTypes(@Nullable Map<?, ?> map) {
574
    if (map == null) {
1✔
575
      return null;
×
576
    }
577
    // Not using ImmutableMap.Builder because of extra guava dependency for Android.
578
    Map<String, Object> parsedMap = new LinkedHashMap<>();
1✔
579
    for (Map.Entry<?, ?> entry : map.entrySet()) {
1✔
580
      checkArgument(
1✔
581
          entry.getKey() instanceof String,
1✔
582
          "The key of the entry '%s' is not of String type", entry);
583

584
      String key = (String) entry.getKey();
1✔
585
      Object value = entry.getValue();
1✔
586
      if (value == null) {
1✔
587
        parsedMap.put(key, null);
1✔
588
      } else if (value instanceof Map) {
1✔
589
        parsedMap.put(key, checkMapEntryTypes((Map<?, ?>) value));
1✔
590
      } else if (value instanceof List) {
1✔
591
        parsedMap.put(key, checkListEntryTypes((List<?>) value));
1✔
592
      } else if (value instanceof String) {
1✔
593
        parsedMap.put(key, value);
1✔
594
      } else if (value instanceof Double) {
1✔
595
        parsedMap.put(key, value);
1✔
596
      } else if (value instanceof Boolean) {
1✔
597
        parsedMap.put(key, value);
1✔
598
      } else {
599
        throw new IllegalArgumentException(
1✔
600
            "The value of the map entry '" + entry + "' is of type '" + value.getClass()
1✔
601
                + "', which is not supported");
602
      }
603
    }
1✔
604
    return Collections.unmodifiableMap(parsedMap);
1✔
605
  }
606

607
  private static List<?> checkListEntryTypes(List<?> list) {
608
    List<Object> parsedList = new ArrayList<>(list.size());
1✔
609
    for (Object value : list) {
1✔
610
      if (value == null) {
1✔
611
        parsedList.add(null);
1✔
612
      } else if (value instanceof Map) {
1✔
613
        parsedList.add(checkMapEntryTypes((Map<?, ?>) value));
1✔
614
      } else if (value instanceof List) {
1✔
615
        parsedList.add(checkListEntryTypes((List<?>) value));
×
616
      } else if (value instanceof String) {
1✔
617
        parsedList.add(value);
1✔
618
      } else if (value instanceof Double) {
1✔
619
        parsedList.add(value);
1✔
620
      } else if (value instanceof Boolean) {
1✔
621
        parsedList.add(value);
1✔
622
      } else {
623
        throw new IllegalArgumentException(
×
624
            "The entry '" + value + "' is of type '" + value.getClass()
×
625
                + "', which is not supported");
626
      }
627
    }
1✔
628
    return Collections.unmodifiableList(parsedList);
1✔
629
  }
630

631
  @Override
632
  public <X> ManagedChannelImplBuilder setNameResolverArg(NameResolver.Args.Key<X> key, X value) {
633
    if (nameResolverCustomArgs == null) {
1✔
634
      nameResolverCustomArgs = new IdentityHashMap<>();
1✔
635
    }
636
    nameResolverCustomArgs.put(checkNotNull(key, "key"), checkNotNull(value, "value"));
1✔
637
    return this;
1✔
638
  }
639

640
  @SuppressWarnings("unchecked") // This cast is safe because of setNameResolverArg()'s signature.
641
  void copyAllNameResolverCustomArgsTo(NameResolver.Args.Builder dest) {
642
    if (nameResolverCustomArgs != null) {
1✔
643
      for (Map.Entry<NameResolver.Args.Key<?>, Object> entry : nameResolverCustomArgs.entrySet()) {
1✔
644
        dest.setArg((NameResolver.Args.Key<Object>) entry.getKey(), entry.getValue());
1✔
645
      }
1✔
646
    }
647
  }
1✔
648

649
  @Override
650
  public ManagedChannelImplBuilder disableServiceConfigLookUp() {
651
    this.lookUpServiceConfig = false;
1✔
652
    return this;
1✔
653
  }
654

655
  /**
656
   * Disable or enable stats features. Enabled by default.
657
   *
658
   * <p>For the current release, calling {@code setStatsEnabled(true)} may have a side effect that
659
   * disables retry.
660
   */
661
  public void setStatsEnabled(boolean value) {
662
    statsEnabled = value;
1✔
663
  }
1✔
664

665
  /**
666
   * Disable or enable stats recording for RPC upstarts.  Effective only if {@link
667
   * #setStatsEnabled} is set to true.  Enabled by default.
668
   */
669
  public void setStatsRecordStartedRpcs(boolean value) {
670
    recordStartedRpcs = value;
1✔
671
  }
1✔
672

673
  /**
674
   * Disable or enable stats recording for RPC completions.  Effective only if {@link
675
   * #setStatsEnabled} is set to true.  Enabled by default.
676
   */
677
  public void setStatsRecordFinishedRpcs(boolean value) {
678
    recordFinishedRpcs = value;
1✔
679
  }
1✔
680

681
  /**
682
   * Disable or enable real-time metrics recording.  Effective only if {@link #setStatsEnabled} is
683
   * set to true.  Disabled by default.
684
   */
685
  public void setStatsRecordRealTimeMetrics(boolean value) {
686
    recordRealTimeMetrics = value;
×
687
  }
×
688
  
689
  public void setStatsRecordRetryMetrics(boolean value) {
690
    recordRetryMetrics = value;
1✔
691
  }
1✔
692

693
  /**
694
   * Disable or enable tracing features.  Enabled by default.
695
   */
696
  public void setTracingEnabled(boolean value) {
697
    tracingEnabled = value;
1✔
698
  }
1✔
699

700
  /**
701
   * Verifies the authority is valid.
702
   */
703
  @VisibleForTesting
704
  String checkAuthority(String authority) {
705
    if (authorityCheckerDisabled) {
1✔
706
      return authority;
1✔
707
    }
708
    return GrpcUtil.checkAuthority(authority);
1✔
709
  }
710

711
  /** Disable the check whether the authority is valid. */
712
  public ManagedChannelImplBuilder disableCheckAuthority() {
713
    authorityCheckerDisabled = true;
1✔
714
    return this;
1✔
715
  }
716

717
  /** Enable previously disabled authority check. */
718
  public ManagedChannelImplBuilder enableCheckAuthority() {
719
    authorityCheckerDisabled = false;
1✔
720
    return this;
1✔
721
  }
722

723
  @Override
724
  protected ManagedChannelImplBuilder addMetricSink(MetricSink metricSink) {
725
    metricSinks.add(checkNotNull(metricSink, "metric sink"));
1✔
726
    return this;
1✔
727
  }
728

729
  @Override
730
  public ManagedChannel build() {
731
    ClientTransportFactory clientTransportFactory =
1✔
732
        clientTransportFactoryBuilder.buildClientTransportFactory();
1✔
733
    ResolvedNameResolver resolvedResolver =
734
        enableRfc3986Uris
1✔
735
            ? getNameResolverProviderRfc3986(target, nameResolverRegistry)
1✔
736
            : getNameResolverProvider(target, nameResolverRegistry);
1✔
737
    resolvedResolver.checkAddressTypes(clientTransportFactory.getSupportedSocketAddressTypes());
1✔
738
    return new ManagedChannelOrphanWrapper(new ManagedChannelImpl(
1✔
739
        this,
740
        clientTransportFactory,
741
        resolvedResolver.targetUri,
742
        resolvedResolver.provider,
743
        new ExponentialBackoffPolicy.Provider(),
744
        SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR),
1✔
745
        GrpcUtil.STOPWATCH_SUPPLIER,
746
        getEffectiveInterceptors(resolvedResolver.targetUri.toString()),
1✔
747
        TimeProvider.SYSTEM_TIME_PROVIDER));
748
  }
749

750
  // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know
751
  // what should be the desired behavior for retry + stats/tracing.
752
  // TODO(zdapeng): FIX IT
753
  @VisibleForTesting
754
  List<ClientInterceptor> getEffectiveInterceptors(String computedTarget) {
755
    List<ClientInterceptor> effectiveInterceptors = new ArrayList<>(this.interceptors.size());
1✔
756
    for (ClientInterceptor interceptor : this.interceptors) {
1✔
757
      if (interceptor instanceof InterceptorFactoryWrapper) {
1✔
758
        InterceptorFactory factory = ((InterceptorFactoryWrapper) interceptor).factory;
1✔
759
        interceptor = factory.newInterceptor(computedTarget);
1✔
760
        if (interceptor == null) {
1✔
761
          throw new NullPointerException("Factory returned null interceptor: " + factory);
×
762
        }
763
      }
764
      effectiveInterceptors.add(interceptor);
1✔
765
    }
1✔
766

767
    boolean disableImplicitCensus = InternalConfiguratorRegistry.wasSetConfiguratorsCalled();
1✔
768
    if (disableImplicitCensus) {
1✔
769
      return effectiveInterceptors;
×
770
    }
771
    if (statsEnabled) {
1✔
772
      ClientInterceptor statsInterceptor = null;
1✔
773

774
      if (GET_CLIENT_INTERCEPTOR_METHOD != null) {
1✔
775
        try {
776
          statsInterceptor =
1✔
777
              (ClientInterceptor) GET_CLIENT_INTERCEPTOR_METHOD
778
              .invoke(
1✔
779
                null,
780
                recordStartedRpcs,
1✔
781
                recordFinishedRpcs,
1✔
782
                recordRealTimeMetrics,
1✔
783
                recordRetryMetrics);
1✔
784
        } catch (IllegalAccessException e) {
×
785
          log.log(Level.FINE, "Unable to apply census stats", e);
×
786
        } catch (InvocationTargetException e) {
×
787
          log.log(Level.FINE, "Unable to apply census stats", e);
×
788
        }
1✔
789
      }
790

791
      if (statsInterceptor != null) {
1✔
792
        // First interceptor runs last (see ClientInterceptors.intercept()), so that no
793
        // other interceptor can override the tracer factory we set in CallOptions.
794
        effectiveInterceptors.add(0, statsInterceptor);
1✔
795
      }
796
    }
797
    if (tracingEnabled) {
1✔
798
      ClientInterceptor tracingInterceptor = null;
1✔
799
      try {
800
        Class<?> censusTracingAccessor =
1✔
801
            Class.forName("io.grpc.census.InternalCensusTracingAccessor");
1✔
802
        Method getClientInterceptroMethod =
1✔
803
            censusTracingAccessor.getDeclaredMethod("getClientInterceptor");
1✔
804
        tracingInterceptor = (ClientInterceptor) getClientInterceptroMethod.invoke(null);
1✔
805
      } catch (ClassNotFoundException e) {
1✔
806
        // Replace these separate catch statements with multicatch when Android min-API >= 19
807
        log.log(Level.FINE, "Unable to apply census stats", e);
1✔
808
      } catch (NoSuchMethodException e) {
×
809
        log.log(Level.FINE, "Unable to apply census stats", e);
×
810
      } catch (IllegalAccessException e) {
×
811
        log.log(Level.FINE, "Unable to apply census stats", e);
×
812
      } catch (InvocationTargetException e) {
×
813
        log.log(Level.FINE, "Unable to apply census stats", e);
×
814
      }
1✔
815
      if (tracingInterceptor != null) {
1✔
816
        effectiveInterceptors.add(0, tracingInterceptor);
1✔
817
      }
818
    }
819
    return effectiveInterceptors;
1✔
820
  }
821

822
  /**
823
   * Returns a default port to {@link NameResolver} for use in cases where the target string doesn't
824
   * include a port. The default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}.
825
   */
826
  int getDefaultPort() {
827
    return channelBuilderDefaultPortProvider.getDefaultPort();
1✔
828
  }
829

830
  @VisibleForTesting
831
  static class ResolvedNameResolver {
832
    public final UriWrapper targetUri;
833
    public final NameResolverProvider provider;
834

835
    public ResolvedNameResolver(UriWrapper targetUri, NameResolverProvider provider) {
1✔
836
      this.targetUri = checkNotNull(targetUri, "targetUri");
1✔
837
      this.provider = checkNotNull(provider, "provider");
1✔
838
    }
1✔
839

840
    void checkAddressTypes(
841
        Collection<Class<? extends SocketAddress>> channelTransportSocketAddressTypes) {
842
      if (channelTransportSocketAddressTypes != null) {
1✔
843
        Collection<Class<? extends SocketAddress>> nameResolverSocketAddressTypes =
1✔
844
            provider.getProducedSocketAddressTypes();
1✔
845
        if (!channelTransportSocketAddressTypes.containsAll(nameResolverSocketAddressTypes)) {
1✔
846
          throw new IllegalArgumentException(
1✔
847
              String.format(
1✔
848
                  "Address types of NameResolver '%s' for '%s' not supported by transport",
849
                  provider.getDefaultScheme(), targetUri));
1✔
850
        }
851
      }
852
    }
1✔
853
  }
854

855
  @VisibleForTesting
856
  static ResolvedNameResolver getNameResolverProvider(
857
      String target, NameResolverRegistry nameResolverRegistry) {
858
    // Finding a NameResolver. Try using the target string as the URI. If that fails, try prepending
859
    // "dns:///".
860
    NameResolverProvider provider = null;
1✔
861
    URI targetUri = null;
1✔
862
    StringBuilder uriSyntaxErrors = new StringBuilder();
1✔
863
    try {
864
      targetUri = new URI(target);
1✔
865
    } catch (URISyntaxException e) {
1✔
866
      // Can happen with ip addresses like "[::1]:1234" or 127.0.0.1:1234.
867
      uriSyntaxErrors.append(e.getMessage());
1✔
868
    }
1✔
869
    if (targetUri != null) {
1✔
870
      // For "localhost:8080" this would likely cause provider to be null, because "localhost" is
871
      // parsed as the scheme. Will hit the next case and try "dns:///localhost:8080".
872
      provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
873
    }
874

875
    if (provider == null && !URI_PATTERN.matcher(target).matches()) {
1✔
876
      // It doesn't look like a URI target. Maybe it's an authority string. Try with the default
877
      // scheme from the registry.
878
      try {
879
        targetUri = new URI(nameResolverRegistry.getDefaultScheme(), "", "/" + target, null);
1✔
880
      } catch (URISyntaxException e) {
×
881
        // Should not be possible.
882
        throw new IllegalArgumentException(e);
×
883
      }
1✔
884
      provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
885
    }
886

887
    if (provider == null) {
1✔
888
      throw new IllegalArgumentException(String.format(
1✔
889
          "Could not find a NameResolverProvider for %s%s",
890
          target, uriSyntaxErrors.length() > 0 ? " (" + uriSyntaxErrors + ")" : ""));
1✔
891
    }
892

893
    return new ResolvedNameResolver(wrap(targetUri), provider);
1✔
894
  }
895

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

916
    if (provider == null && !URI_PATTERN.matcher(target).matches()) {
1✔
917
      // It doesn't look like a URI target. Maybe it's an authority string. Try with the default
918
      // scheme from the registry.
919
      targetUri =
920
          Uri.newBuilder()
1✔
921
              .setScheme(nameResolverRegistry.getDefaultScheme())
1✔
922
              .setHost("")
1✔
923
              .setPath("/" + target)
1✔
924
              .build();
1✔
925
      provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
926
    }
927

928
    if (provider == null) {
1✔
929
      throw new IllegalArgumentException(
1✔
930
          String.format(
1✔
931
              "Could not find a NameResolverProvider for %s%s",
932
              target, uriSyntaxErrors.length() > 0 ? " (" + uriSyntaxErrors + ")" : ""));
1✔
933
    }
934

935
    return new ResolvedNameResolver(wrap(targetUri), provider);
1✔
936
  }
937

938
  private static class DirectAddressNameResolverProvider extends NameResolverProvider {
939
    final SocketAddress address;
940
    final String authority;
941
    final Collection<Class<? extends SocketAddress>> producedSocketAddressTypes;
942

943
    DirectAddressNameResolverProvider(SocketAddress address, String authority) {
1✔
944
      this.address = address;
1✔
945
      this.authority = authority;
1✔
946
      this.producedSocketAddressTypes
1✔
947
          = Collections.singleton(address.getClass());
1✔
948
    }
1✔
949

950
    @Override
951
    public NameResolver newNameResolver(URI notUsedUri, NameResolver.Args args) {
952
      return new NameResolver() {
1✔
953
        @Override
954
        public String getServiceAuthority() {
955
          return authority;
1✔
956
        }
957

958
        @Override
959
        public void start(Listener2 listener) {
960
          listener.onResult2(
1✔
961
              ResolutionResult.newBuilder()
1✔
962
                  .setAddressesOrError(
1✔
963
                      StatusOr.fromValue(
1✔
964
                          Collections.singletonList(new EquivalentAddressGroup(address))))
1✔
965
                  .setAttributes(Attributes.EMPTY)
1✔
966
                  .build());
1✔
967
        }
1✔
968

969
        @Override
970
        public void shutdown() {}
1✔
971
      };
972
    }
973

974
    @Override
975
    public String getDefaultScheme() {
976
      return DIRECT_ADDRESS_SCHEME;
1✔
977
    }
978

979
    @Override
980
    protected boolean isAvailable() {
981
      return true;
1✔
982
    }
983

984
    @Override
985
    protected int priority() {
986
      return 5;
1✔
987
    }
988

989
    @Override
990
    public Collection<Class<? extends SocketAddress>> getProducedSocketAddressTypes() {
991
      return producedSocketAddressTypes;
1✔
992
    }
993
  }
994

995
  private static final class InterceptorFactoryWrapper implements ClientInterceptor {
996
    final InterceptorFactory factory;
997

998
    public InterceptorFactoryWrapper(InterceptorFactory factory) {
1✔
999
      this.factory = checkNotNull(factory, "factory");
1✔
1000
    }
1✔
1001

1002
    @Override
1003
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
1004
        MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
1005
      throw new AssertionError("Should have been replaced with real instance");
×
1006
    }
1007
  }
1008

1009
  /**
1010
   * Returns the internal offload executor pool for offloading tasks.
1011
   */
1012
  public ObjectPool<? extends Executor> getOffloadExecutorPool() {
1013
    return this.offloadExecutorPool;
1✔
1014
  }
1015
}
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