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

grpc / grpc-java / #19497

07 Oct 2024 12:25PM UTC coverage: 84.656% (+0.06%) from 84.599%
#19497

push

github

web-flow
On result2 resolution result have addresses or error (#11330)

Combined success / error status passed via ResolutionResult to the NameResolver.Listener2 interface's onResult2 method - Addresses in the success case or address resolution error in the failure case now get set in ResolutionResult::addressesOrError by the internal name resolvers.

33771 of 39892 relevant lines covered (84.66%)

0.85 hits per line

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

90.96
/../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

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

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

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

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

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

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

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

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

106
  private static final ObjectPool<? extends Executor> DEFAULT_EXECUTOR_POOL =
1✔
107
      SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR);
1✔
108

109
  private static final DecompressorRegistry DEFAULT_DECOMPRESSOR_REGISTRY =
110
      DecompressorRegistry.getDefaultInstance();
1✔
111

112
  private static final CompressorRegistry DEFAULT_COMPRESSOR_REGISTRY =
113
      CompressorRegistry.getDefaultInstance();
1✔
114

115
  private static final long DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES = 1L << 24;  // 16M
116
  private static final long DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES = 1L << 20; // 1M
117

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

124
  private static final Method GET_CLIENT_INTERCEPTOR_METHOD;
125

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

147

148
  ObjectPool<? extends Executor> executorPool = DEFAULT_EXECUTOR_POOL;
1✔
149

150
  ObjectPool<? extends Executor> offloadExecutorPool = DEFAULT_EXECUTOR_POOL;
1✔
151

152
  private final List<ClientInterceptor> interceptors = new ArrayList<>();
1✔
153
  NameResolverRegistry nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
1✔
154

155
  final List<ClientTransportFilter> transportFilters = new ArrayList<>();
1✔
156

157
  final String target;
158
  @Nullable
159
  final ChannelCredentials channelCredentials;
160
  @Nullable
161
  final CallCredentials callCredentials;
162

163
  @Nullable
164
  private final SocketAddress directServerAddress;
165

166
  @Nullable
167
  String userAgent;
168

169
  @Nullable
170
  String authorityOverride;
171

172
  String defaultLbPolicy = GrpcUtil.DEFAULT_LB_POLICY;
1✔
173

174
  boolean fullStreamDecompression;
175

176
  DecompressorRegistry decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
1✔
177

178
  CompressorRegistry compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
1✔
179

180
  long idleTimeoutMillis = IDLE_MODE_DEFAULT_TIMEOUT_MILLIS;
1✔
181

182
  int maxRetryAttempts = 5;
1✔
183
  int maxHedgedAttempts = 5;
1✔
184
  long retryBufferSize = DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES;
1✔
185
  long perRpcBufferLimit = DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES;
1✔
186
  boolean retryEnabled = true;
1✔
187

188
  InternalChannelz channelz = InternalChannelz.instance();
1✔
189
  int maxTraceEvents;
190

191
  @Nullable
192
  Map<String, ?> defaultServiceConfig;
193
  boolean lookUpServiceConfig = true;
1✔
194

195
  @Nullable
196
  BinaryLog binlog;
197

198
  @Nullable
199
  ProxyDetector proxyDetector;
200

201
  private boolean authorityCheckerDisabled;
202
  private boolean statsEnabled = true;
1✔
203
  private boolean recordStartedRpcs = true;
1✔
204
  private boolean recordFinishedRpcs = true;
1✔
205
  private boolean recordRealTimeMetrics = false;
1✔
206
  private boolean recordRetryMetrics = true;
1✔
207
  private boolean tracingEnabled = true;
1✔
208
  List<MetricSink> metricSinks = new ArrayList<>();
1✔
209

210
  /**
211
   * An interface for Transport implementors to provide the {@link ClientTransportFactory}
212
   * appropriate for the channel.
213
   */
214
  public interface ClientTransportFactoryBuilder {
215
    ClientTransportFactory buildClientTransportFactory();
216
  }
217

218
  /**
219
   * Convenience ClientTransportFactoryBuilder, throws UnsupportedOperationException().
220
   */
221
  public static class UnsupportedClientTransportFactoryBuilder implements
1✔
222
      ClientTransportFactoryBuilder {
223
    @Override
224
    public ClientTransportFactory buildClientTransportFactory() {
225
      throw new UnsupportedOperationException();
×
226
    }
227
  }
228

229
  /**
230
   * An interface for Transport implementors to provide a default port to {@link
231
   * io.grpc.NameResolver} for use in cases where the target string doesn't include a port. The
232
   * default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}.
233
   */
234
  public interface ChannelBuilderDefaultPortProvider {
235
    int getDefaultPort();
236
  }
237

238
  /**
239
   * Default implementation of {@link ChannelBuilderDefaultPortProvider} that returns a fixed port.
240
   */
241
  public static final class FixedPortProvider implements ChannelBuilderDefaultPortProvider {
242
    private final int port;
243

244
    public FixedPortProvider(int port) {
1✔
245
      this.port = port;
1✔
246
    }
1✔
247

248
    @Override
249
    public int getDefaultPort() {
250
      return port;
1✔
251
    }
252
  }
253

254
  private static final class ManagedChannelDefaultPortProvider implements
255
      ChannelBuilderDefaultPortProvider {
256
    @Override
257
    public int getDefaultPort() {
258
      return GrpcUtil.DEFAULT_PORT_SSL;
1✔
259
    }
260
  }
261

262
  private final ClientTransportFactoryBuilder clientTransportFactoryBuilder;
263
  private final ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider;
264

265
  /**
266
   * Creates a new managed channel builder with a target string, which can be either a valid {@link
267
   * io.grpc.NameResolver}-compliant URI, or an authority string. Transport implementors must
268
   * provide client transport factory builder, and may set custom channel default port provider.
269
   */
270
  public ManagedChannelImplBuilder(String target,
271
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
272
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
273
    this(target, null, null, clientTransportFactoryBuilder, channelBuilderDefaultPortProvider);
1✔
274
  }
1✔
275

276
  /**
277
   * Creates a new managed channel builder with a target string, which can be either a valid {@link
278
   * io.grpc.NameResolver}-compliant URI, or an authority string. Transport implementors must
279
   * provide client transport factory builder, and may set custom channel default port provider.
280
   *
281
   * @param channelCreds The ChannelCredentials provided by the user. These may be used when
282
   *     creating derivative channels.
283
   */
284
  public ManagedChannelImplBuilder(
285
      String target, @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds,
286
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
287
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
1✔
288
    this.target = checkNotNull(target, "target");
1✔
289
    this.channelCredentials = channelCreds;
1✔
290
    this.callCredentials = callCreds;
1✔
291
    this.clientTransportFactoryBuilder = checkNotNull(clientTransportFactoryBuilder,
1✔
292
        "clientTransportFactoryBuilder");
293
    this.directServerAddress = null;
1✔
294

295
    if (channelBuilderDefaultPortProvider != null) {
1✔
296
      this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider;
1✔
297
    } else {
298
      this.channelBuilderDefaultPortProvider = new ManagedChannelDefaultPortProvider();
1✔
299
    }
300
    // TODO(dnvindhya): Move configurator to all the individual builders
301
    InternalConfiguratorRegistry.configureChannelBuilder(this);
1✔
302
  }
1✔
303

304
  /**
305
   * Returns a target string for the SocketAddress. It is only used as a placeholder, because
306
   * DirectAddressNameResolverProvider will not actually try to use it. However, it must be a valid
307
   * URI.
308
   */
309
  @VisibleForTesting
310
  static String makeTargetStringForDirectAddress(SocketAddress address) {
311
    try {
312
      return new URI(DIRECT_ADDRESS_SCHEME, "", "/" + address, null).toString();
1✔
313
    } catch (URISyntaxException e) {
×
314
      // It should not happen.
315
      throw new RuntimeException(e);
×
316
    }
317
  }
318

319
  /**
320
   * Creates a new managed channel builder with the given server address, authority string of the
321
   * channel. Transport implementors must provide client transport factory builder, and may set
322
   * custom channel default port provider.
323
   */
324
  public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
325
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
326
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
327
    this(directServerAddress, authority, null, null, clientTransportFactoryBuilder,
1✔
328
        channelBuilderDefaultPortProvider);
329
  }
1✔
330

331
  /**
332
   * Creates a new managed channel builder with the given server address, authority string of the
333
   * channel. Transport implementors must provide client transport factory builder, and may set
334
   * custom channel default port provider.
335
   * 
336
   * @param channelCreds The ChannelCredentials provided by the user. These may be used when
337
   *     creating derivative channels.
338
   */
339
  public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
340
      @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds,
341
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
342
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
1✔
343
    this.target = makeTargetStringForDirectAddress(directServerAddress);
1✔
344
    this.channelCredentials = channelCreds;
1✔
345
    this.callCredentials = callCreds;
1✔
346
    this.clientTransportFactoryBuilder = checkNotNull(clientTransportFactoryBuilder,
1✔
347
        "clientTransportFactoryBuilder");
348
    this.directServerAddress = directServerAddress;
1✔
349
    NameResolverRegistry reg = new NameResolverRegistry();
1✔
350
    reg.register(new DirectAddressNameResolverProvider(directServerAddress,
1✔
351
        authority));
352
    this.nameResolverRegistry = reg;
1✔
353

354
    if (channelBuilderDefaultPortProvider != null) {
1✔
355
      this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider;
1✔
356
    } else {
357
      this.channelBuilderDefaultPortProvider = new ManagedChannelDefaultPortProvider();
1✔
358
    }
359
    // TODO(dnvindhya): Move configurator to all the individual builders
360
    InternalConfiguratorRegistry.configureChannelBuilder(this);
1✔
361
  }
1✔
362

363
  @Override
364
  public ManagedChannelImplBuilder directExecutor() {
365
    return executor(MoreExecutors.directExecutor());
1✔
366
  }
367

368
  @Override
369
  public ManagedChannelImplBuilder executor(Executor executor) {
370
    if (executor != null) {
1✔
371
      this.executorPool = new FixedObjectPool<>(executor);
1✔
372
    } else {
373
      this.executorPool = DEFAULT_EXECUTOR_POOL;
1✔
374
    }
375
    return this;
1✔
376
  }
377

378
  @Override
379
  public ManagedChannelImplBuilder offloadExecutor(Executor executor) {
380
    if (executor != null) {
1✔
381
      this.offloadExecutorPool = new FixedObjectPool<>(executor);
1✔
382
    } else {
383
      this.offloadExecutorPool = DEFAULT_EXECUTOR_POOL;
1✔
384
    }
385
    return this;
1✔
386
  }
387

388
  @Override
389
  public ManagedChannelImplBuilder intercept(List<ClientInterceptor> interceptors) {
390
    this.interceptors.addAll(interceptors);
1✔
391
    return this;
1✔
392
  }
393

394
  @Override
395
  public ManagedChannelImplBuilder intercept(ClientInterceptor... interceptors) {
396
    return intercept(Arrays.asList(interceptors));
1✔
397
  }
398

399
  @Override
400
  protected ManagedChannelImplBuilder interceptWithTarget(InterceptorFactory factory) {
401
    // Add a placeholder instance to the interceptor list, and replace it with a real instance
402
    // during build().
403
    this.interceptors.add(new InterceptorFactoryWrapper(factory));
1✔
404
    return this;
1✔
405
  }
406

407
  @Override
408
  public ManagedChannelImplBuilder addTransportFilter(ClientTransportFilter hook) {
409
    transportFilters.add(checkNotNull(hook, "transport filter"));
1✔
410
    return this;
1✔
411
  }
412

413
  @Deprecated
414
  @Override
415
  public ManagedChannelImplBuilder nameResolverFactory(NameResolver.Factory resolverFactory) {
416
    Preconditions.checkState(directServerAddress == null,
1✔
417
        "directServerAddress is set (%s), which forbids the use of NameResolverFactory",
418
        directServerAddress);
419
    if (resolverFactory != null) {
1✔
420
      NameResolverRegistry reg = new NameResolverRegistry();
1✔
421
      if (resolverFactory instanceof NameResolverProvider) {
1✔
422
        reg.register((NameResolverProvider) resolverFactory);
1✔
423
      } else {
424
        reg.register(new NameResolverFactoryToProviderFacade(resolverFactory));
1✔
425
      }
426
      this.nameResolverRegistry = reg;
1✔
427
    } else {
1✔
428
      this.nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
1✔
429
    }
430
    return this;
1✔
431
  }
432

433
  ManagedChannelImplBuilder nameResolverRegistry(NameResolverRegistry resolverRegistry) {
434
    this.nameResolverRegistry = resolverRegistry;
1✔
435
    return this;
1✔
436
  }
437

438
  @Override
439
  public ManagedChannelImplBuilder defaultLoadBalancingPolicy(String policy) {
440
    Preconditions.checkState(directServerAddress == null,
1✔
441
        "directServerAddress is set (%s), which forbids the use of load-balancing policy",
442
        directServerAddress);
443
    Preconditions.checkArgument(policy != null, "policy cannot be null");
1✔
444
    this.defaultLbPolicy = policy;
1✔
445
    return this;
1✔
446
  }
447

448
  @Override
449
  public ManagedChannelImplBuilder decompressorRegistry(DecompressorRegistry registry) {
450
    if (registry != null) {
1✔
451
      this.decompressorRegistry = registry;
1✔
452
    } else {
453
      this.decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
1✔
454
    }
455
    return this;
1✔
456
  }
457

458
  @Override
459
  public ManagedChannelImplBuilder compressorRegistry(CompressorRegistry registry) {
460
    if (registry != null) {
1✔
461
      this.compressorRegistry = registry;
1✔
462
    } else {
463
      this.compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
1✔
464
    }
465
    return this;
1✔
466
  }
467

468
  @Override
469
  public ManagedChannelImplBuilder userAgent(@Nullable String userAgent) {
470
    this.userAgent = userAgent;
1✔
471
    return this;
1✔
472
  }
473

474
  @Override
475
  public ManagedChannelImplBuilder overrideAuthority(String authority) {
476
    this.authorityOverride = checkAuthority(authority);
1✔
477
    return this;
1✔
478
  }
479

480
  @Override
481
  public ManagedChannelImplBuilder idleTimeout(long value, TimeUnit unit) {
482
    checkArgument(value > 0, "idle timeout is %s, but must be positive", value);
1✔
483
    // We convert to the largest unit to avoid overflow
484
    if (unit.toDays(value) >= IDLE_MODE_MAX_TIMEOUT_DAYS) {
1✔
485
      // This disables idle mode
486
      this.idleTimeoutMillis = ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE;
1✔
487
    } else {
488
      this.idleTimeoutMillis = Math.max(unit.toMillis(value), IDLE_MODE_MIN_TIMEOUT_MILLIS);
1✔
489
    }
490
    return this;
1✔
491
  }
492

493
  @Override
494
  public ManagedChannelImplBuilder maxRetryAttempts(int maxRetryAttempts) {
495
    this.maxRetryAttempts = maxRetryAttempts;
1✔
496
    return this;
1✔
497
  }
498

499
  @Override
500
  public ManagedChannelImplBuilder maxHedgedAttempts(int maxHedgedAttempts) {
501
    this.maxHedgedAttempts = maxHedgedAttempts;
1✔
502
    return this;
1✔
503
  }
504

505
  @Override
506
  public ManagedChannelImplBuilder retryBufferSize(long bytes) {
507
    checkArgument(bytes > 0L, "retry buffer size must be positive");
1✔
508
    retryBufferSize = bytes;
1✔
509
    return this;
1✔
510
  }
511

512
  @Override
513
  public ManagedChannelImplBuilder perRpcBufferLimit(long bytes) {
514
    checkArgument(bytes > 0L, "per RPC buffer limit must be positive");
1✔
515
    perRpcBufferLimit = bytes;
1✔
516
    return this;
1✔
517
  }
518

519
  @Override
520
  public ManagedChannelImplBuilder disableRetry() {
521
    retryEnabled = false;
1✔
522
    return this;
1✔
523
  }
524

525
  @Override
526
  public ManagedChannelImplBuilder enableRetry() {
527
    retryEnabled = true;
1✔
528
    return this;
1✔
529
  }
530

531
  @Override
532
  public ManagedChannelImplBuilder setBinaryLog(BinaryLog binlog) {
533
    this.binlog = binlog;
×
534
    return this;
×
535
  }
536

537
  @Override
538
  public ManagedChannelImplBuilder maxTraceEvents(int maxTraceEvents) {
539
    checkArgument(maxTraceEvents >= 0, "maxTraceEvents must be non-negative");
1✔
540
    this.maxTraceEvents = maxTraceEvents;
1✔
541
    return this;
1✔
542
  }
543

544
  @Override
545
  public ManagedChannelImplBuilder proxyDetector(@Nullable ProxyDetector proxyDetector) {
546
    this.proxyDetector = proxyDetector;
1✔
547
    return this;
1✔
548
  }
549

550
  @Override
551
  public ManagedChannelImplBuilder defaultServiceConfig(@Nullable Map<String, ?> serviceConfig) {
552
    // TODO(notcarl): use real parsing
553
    defaultServiceConfig = checkMapEntryTypes(serviceConfig);
1✔
554
    return this;
1✔
555
  }
556

557
  @Nullable
558
  private static Map<String, ?> checkMapEntryTypes(@Nullable Map<?, ?> map) {
559
    if (map == null) {
1✔
560
      return null;
×
561
    }
562
    // Not using ImmutableMap.Builder because of extra guava dependency for Android.
563
    Map<String, Object> parsedMap = new LinkedHashMap<>();
1✔
564
    for (Map.Entry<?, ?> entry : map.entrySet()) {
1✔
565
      checkArgument(
1✔
566
          entry.getKey() instanceof String,
1✔
567
          "The key of the entry '%s' is not of String type", entry);
568

569
      String key = (String) entry.getKey();
1✔
570
      Object value = entry.getValue();
1✔
571
      if (value == null) {
1✔
572
        parsedMap.put(key, null);
1✔
573
      } else if (value instanceof Map) {
1✔
574
        parsedMap.put(key, checkMapEntryTypes((Map<?, ?>) value));
1✔
575
      } else if (value instanceof List) {
1✔
576
        parsedMap.put(key, checkListEntryTypes((List<?>) value));
1✔
577
      } else if (value instanceof String) {
1✔
578
        parsedMap.put(key, value);
1✔
579
      } else if (value instanceof Double) {
1✔
580
        parsedMap.put(key, value);
1✔
581
      } else if (value instanceof Boolean) {
1✔
582
        parsedMap.put(key, value);
1✔
583
      } else {
584
        throw new IllegalArgumentException(
1✔
585
            "The value of the map entry '" + entry + "' is of type '" + value.getClass()
1✔
586
                + "', which is not supported");
587
      }
588
    }
1✔
589
    return Collections.unmodifiableMap(parsedMap);
1✔
590
  }
591

592
  private static List<?> checkListEntryTypes(List<?> list) {
593
    List<Object> parsedList = new ArrayList<>(list.size());
1✔
594
    for (Object value : list) {
1✔
595
      if (value == null) {
1✔
596
        parsedList.add(null);
1✔
597
      } else if (value instanceof Map) {
1✔
598
        parsedList.add(checkMapEntryTypes((Map<?, ?>) value));
1✔
599
      } else if (value instanceof List) {
1✔
600
        parsedList.add(checkListEntryTypes((List<?>) value));
×
601
      } else if (value instanceof String) {
1✔
602
        parsedList.add(value);
1✔
603
      } else if (value instanceof Double) {
1✔
604
        parsedList.add(value);
1✔
605
      } else if (value instanceof Boolean) {
1✔
606
        parsedList.add(value);
1✔
607
      } else {
608
        throw new IllegalArgumentException(
×
609
            "The entry '" + value + "' is of type '" + value.getClass()
×
610
                + "', which is not supported");
611
      }
612
    }
1✔
613
    return Collections.unmodifiableList(parsedList);
1✔
614
  }
615

616
  @Override
617
  public ManagedChannelImplBuilder disableServiceConfigLookUp() {
618
    this.lookUpServiceConfig = false;
1✔
619
    return this;
1✔
620
  }
621

622
  /**
623
   * Disable or enable stats features. Enabled by default.
624
   *
625
   * <p>For the current release, calling {@code setStatsEnabled(true)} may have a side effect that
626
   * disables retry.
627
   */
628
  public void setStatsEnabled(boolean value) {
629
    statsEnabled = value;
1✔
630
  }
1✔
631

632
  /**
633
   * Disable or enable stats recording for RPC upstarts.  Effective only if {@link
634
   * #setStatsEnabled} is set to true.  Enabled by default.
635
   */
636
  public void setStatsRecordStartedRpcs(boolean value) {
637
    recordStartedRpcs = value;
1✔
638
  }
1✔
639

640
  /**
641
   * Disable or enable stats recording for RPC completions.  Effective only if {@link
642
   * #setStatsEnabled} is set to true.  Enabled by default.
643
   */
644
  public void setStatsRecordFinishedRpcs(boolean value) {
645
    recordFinishedRpcs = value;
1✔
646
  }
1✔
647

648
  /**
649
   * Disable or enable real-time metrics recording.  Effective only if {@link #setStatsEnabled} is
650
   * set to true.  Disabled by default.
651
   */
652
  public void setStatsRecordRealTimeMetrics(boolean value) {
653
    recordRealTimeMetrics = value;
×
654
  }
×
655
  
656
  public void setStatsRecordRetryMetrics(boolean value) {
657
    recordRetryMetrics = value;
1✔
658
  }
1✔
659

660
  /**
661
   * Disable or enable tracing features.  Enabled by default.
662
   */
663
  public void setTracingEnabled(boolean value) {
664
    tracingEnabled = value;
1✔
665
  }
1✔
666

667
  /**
668
   * Verifies the authority is valid.
669
   */
670
  @VisibleForTesting
671
  String checkAuthority(String authority) {
672
    if (authorityCheckerDisabled) {
1✔
673
      return authority;
1✔
674
    }
675
    return GrpcUtil.checkAuthority(authority);
1✔
676
  }
677

678
  /** Disable the check whether the authority is valid. */
679
  public ManagedChannelImplBuilder disableCheckAuthority() {
680
    authorityCheckerDisabled = true;
1✔
681
    return this;
1✔
682
  }
683

684
  /** Enable previously disabled authority check. */
685
  public ManagedChannelImplBuilder enableCheckAuthority() {
686
    authorityCheckerDisabled = false;
1✔
687
    return this;
1✔
688
  }
689

690
  @Override
691
  protected ManagedChannelImplBuilder addMetricSink(MetricSink metricSink) {
692
    metricSinks.add(checkNotNull(metricSink, "metric sink"));
1✔
693
    return this;
1✔
694
  }
695

696
  @Override
697
  public ManagedChannel build() {
698
    ClientTransportFactory clientTransportFactory =
1✔
699
        clientTransportFactoryBuilder.buildClientTransportFactory();
1✔
700
    ResolvedNameResolver resolvedResolver = getNameResolverProvider(
1✔
701
        target, nameResolverRegistry, clientTransportFactory.getSupportedSocketAddressTypes());
1✔
702
    return new ManagedChannelOrphanWrapper(new ManagedChannelImpl(
1✔
703
        this,
704
        clientTransportFactory,
705
        resolvedResolver.targetUri,
706
        resolvedResolver.provider,
707
        new ExponentialBackoffPolicy.Provider(),
708
        SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR),
1✔
709
        GrpcUtil.STOPWATCH_SUPPLIER,
710
        getEffectiveInterceptors(resolvedResolver.targetUri.toString()),
1✔
711
        TimeProvider.SYSTEM_TIME_PROVIDER));
712
  }
713

714
  // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know
715
  // what should be the desired behavior for retry + stats/tracing.
716
  // TODO(zdapeng): FIX IT
717
  @VisibleForTesting
718
  List<ClientInterceptor> getEffectiveInterceptors(String computedTarget) {
719
    List<ClientInterceptor> effectiveInterceptors = new ArrayList<>(this.interceptors);
1✔
720
    for (int i = 0; i < effectiveInterceptors.size(); i++) {
1✔
721
      if (!(effectiveInterceptors.get(i) instanceof InterceptorFactoryWrapper)) {
1✔
722
        continue;
1✔
723
      }
724
      InterceptorFactory factory =
1✔
725
          ((InterceptorFactoryWrapper) effectiveInterceptors.get(i)).factory;
1✔
726
      ClientInterceptor interceptor = factory.newInterceptor(computedTarget);
1✔
727
      if (interceptor == null) {
1✔
728
        throw new NullPointerException("Factory returned null interceptor: " + factory);
×
729
      }
730
      effectiveInterceptors.set(i, interceptor);
1✔
731
    }
732

733
    boolean disableImplicitCensus = InternalConfiguratorRegistry.wasSetConfiguratorsCalled();
1✔
734
    if (disableImplicitCensus) {
1✔
735
      return effectiveInterceptors;
×
736
    }
737
    if (statsEnabled) {
1✔
738
      ClientInterceptor statsInterceptor = null;
1✔
739

740
      if (GET_CLIENT_INTERCEPTOR_METHOD != null) {
1✔
741
        try {
742
          statsInterceptor =
1✔
743
            (ClientInterceptor) GET_CLIENT_INTERCEPTOR_METHOD
744
              .invoke(
1✔
745
                null,
746
                recordStartedRpcs,
1✔
747
                recordFinishedRpcs,
1✔
748
                recordRealTimeMetrics,
1✔
749
                recordRetryMetrics);
1✔
750
        } catch (IllegalAccessException e) {
×
751
          log.log(Level.FINE, "Unable to apply census stats", e);
×
752
        } catch (InvocationTargetException e) {
×
753
          log.log(Level.FINE, "Unable to apply census stats", e);
×
754
        }
1✔
755
      }
756

757
      if (statsInterceptor != null) {
1✔
758
        // First interceptor runs last (see ClientInterceptors.intercept()), so that no
759
        // other interceptor can override the tracer factory we set in CallOptions.
760
        effectiveInterceptors.add(0, statsInterceptor);
1✔
761
      }
762
    }
763
    if (tracingEnabled) {
1✔
764
      ClientInterceptor tracingInterceptor = null;
1✔
765
      try {
766
        Class<?> censusTracingAccessor =
1✔
767
            Class.forName("io.grpc.census.InternalCensusTracingAccessor");
1✔
768
        Method getClientInterceptroMethod =
1✔
769
            censusTracingAccessor.getDeclaredMethod("getClientInterceptor");
1✔
770
        tracingInterceptor = (ClientInterceptor) getClientInterceptroMethod.invoke(null);
1✔
771
      } catch (ClassNotFoundException e) {
1✔
772
        // Replace these separate catch statements with multicatch when Android min-API >= 19
773
        log.log(Level.FINE, "Unable to apply census stats", e);
1✔
774
      } catch (NoSuchMethodException e) {
×
775
        log.log(Level.FINE, "Unable to apply census stats", e);
×
776
      } catch (IllegalAccessException e) {
×
777
        log.log(Level.FINE, "Unable to apply census stats", e);
×
778
      } catch (InvocationTargetException e) {
×
779
        log.log(Level.FINE, "Unable to apply census stats", e);
×
780
      }
1✔
781
      if (tracingInterceptor != null) {
1✔
782
        effectiveInterceptors.add(0, tracingInterceptor);
1✔
783
      }
784
    }
785
    return effectiveInterceptors;
1✔
786
  }
787

788
  /**
789
   * Returns a default port to {@link NameResolver} for use in cases where the target string doesn't
790
   * include a port. The default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}.
791
   */
792
  int getDefaultPort() {
793
    return channelBuilderDefaultPortProvider.getDefaultPort();
1✔
794
  }
795

796
  @VisibleForTesting
797
  static class ResolvedNameResolver {
798
    public final URI targetUri;
799
    public final NameResolverProvider provider;
800

801
    public ResolvedNameResolver(URI targetUri, NameResolverProvider provider) {
1✔
802
      this.targetUri = checkNotNull(targetUri, "targetUri");
1✔
803
      this.provider = checkNotNull(provider, "provider");
1✔
804
    }
1✔
805
  }
806

807
  @VisibleForTesting
808
  static ResolvedNameResolver getNameResolverProvider(
809
      String target, NameResolverRegistry nameResolverRegistry,
810
      Collection<Class<? extends SocketAddress>> channelTransportSocketAddressTypes) {
811
    // Finding a NameResolver. Try using the target string as the URI. If that fails, try prepending
812
    // "dns:///".
813
    NameResolverProvider provider = null;
1✔
814
    URI targetUri = null;
1✔
815
    StringBuilder uriSyntaxErrors = new StringBuilder();
1✔
816
    try {
817
      targetUri = new URI(target);
1✔
818
    } catch (URISyntaxException e) {
1✔
819
      // Can happen with ip addresses like "[::1]:1234" or 127.0.0.1:1234.
820
      uriSyntaxErrors.append(e.getMessage());
1✔
821
    }
1✔
822
    if (targetUri != null) {
1✔
823
      // For "localhost:8080" this would likely cause provider to be null, because "localhost" is
824
      // parsed as the scheme. Will hit the next case and try "dns:///localhost:8080".
825
      provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
826
    }
827

828
    if (provider == null && !URI_PATTERN.matcher(target).matches()) {
1✔
829
      // It doesn't look like a URI target. Maybe it's an authority string. Try with the default
830
      // scheme from the registry.
831
      try {
832
        targetUri = new URI(nameResolverRegistry.getDefaultScheme(), "", "/" + target, null);
1✔
833
      } catch (URISyntaxException e) {
×
834
        // Should not be possible.
835
        throw new IllegalArgumentException(e);
×
836
      }
1✔
837
      provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
838
    }
839

840
    if (provider == null) {
1✔
841
      throw new IllegalArgumentException(String.format(
1✔
842
          "Could not find a NameResolverProvider for %s%s",
843
          target, uriSyntaxErrors.length() > 0 ? " (" + uriSyntaxErrors + ")" : ""));
1✔
844
    }
845

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

856
    return new ResolvedNameResolver(targetUri, provider);
1✔
857
  }
858

859
  private static class DirectAddressNameResolverProvider extends NameResolverProvider {
860
    final SocketAddress address;
861
    final String authority;
862
    final Collection<Class<? extends SocketAddress>> producedSocketAddressTypes;
863

864
    DirectAddressNameResolverProvider(SocketAddress address, String authority) {
1✔
865
      this.address = address;
1✔
866
      this.authority = authority;
1✔
867
      this.producedSocketAddressTypes
1✔
868
          = Collections.singleton(address.getClass());
1✔
869
    }
1✔
870

871
    @Override
872
    public NameResolver newNameResolver(URI notUsedUri, NameResolver.Args args) {
873
      return new NameResolver() {
1✔
874
        @Override
875
        public String getServiceAuthority() {
876
          return authority;
1✔
877
        }
878

879
        @Override
880
        public void start(Listener2 listener) {
881
          listener.onResult2(
1✔
882
              ResolutionResult.newBuilder()
1✔
883
                  .setAddressesOrError(
1✔
884
                      StatusOr.fromValue(
1✔
885
                          Collections.singletonList(new EquivalentAddressGroup(address))))
1✔
886
                  .setAttributes(Attributes.EMPTY)
1✔
887
                  .build());
1✔
888
        }
1✔
889

890
        @Override
891
        public void shutdown() {}
1✔
892
      };
893
    }
894

895
    @Override
896
    public String getDefaultScheme() {
897
      return DIRECT_ADDRESS_SCHEME;
1✔
898
    }
899

900
    @Override
901
    protected boolean isAvailable() {
902
      return true;
1✔
903
    }
904

905
    @Override
906
    protected int priority() {
907
      return 5;
1✔
908
    }
909

910
    @Override
911
    public Collection<Class<? extends SocketAddress>> getProducedSocketAddressTypes() {
912
      return producedSocketAddressTypes;
1✔
913
    }
914
  }
915

916
  private static final class InterceptorFactoryWrapper implements ClientInterceptor {
917
    final InterceptorFactory factory;
918

919
    public InterceptorFactoryWrapper(InterceptorFactory factory) {
1✔
920
      this.factory = checkNotNull(factory, "factory");
1✔
921
    }
1✔
922

923
    @Override
924
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
925
        MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
926
      throw new AssertionError("Should have been replaced with real instance");
×
927
    }
928
  }
929

930
  /**
931
   * Returns the internal offload executor pool for offloading tasks.
932
   */
933
  public ObjectPool<? extends Executor> getOffloadExecutorPool() {
934
    return this.offloadExecutorPool;
1✔
935
  }
936
}
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