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

grpc / grpc-java / #19216

09 May 2024 02:28AM UTC coverage: 88.369% (-0.007%) from 88.376%
#19216

push

github

ejona86
opentelemetry: Add grpc.target label to per-call metrics

As defined by gRFC A66, the target is on all client-side per-call
metrics (both call and attempt).

31538 of 35689 relevant lines covered (88.37%)

0.88 hits per line

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

90.91
/../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 java.lang.reflect.InvocationTargetException;
49
import java.lang.reflect.Method;
50
import java.net.SocketAddress;
51
import java.net.URI;
52
import java.net.URISyntaxException;
53
import java.util.ArrayList;
54
import java.util.Arrays;
55
import java.util.Collection;
56
import java.util.Collections;
57
import java.util.LinkedHashMap;
58
import java.util.List;
59
import java.util.Map;
60
import java.util.concurrent.Executor;
61
import java.util.concurrent.TimeUnit;
62
import java.util.logging.Level;
63
import java.util.logging.Logger;
64
import java.util.regex.Pattern;
65
import javax.annotation.Nullable;
66

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

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

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

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

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

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

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

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

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

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

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

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

123
  private static final Method GET_CLIENT_INTERCEPTOR_METHOD;
124

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

146

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

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

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

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

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

162
  @Nullable
163
  private final SocketAddress directServerAddress;
164

165
  @Nullable
166
  String userAgent;
167

168
  @Nullable
169
  String authorityOverride;
170

171
  String defaultLbPolicy = GrpcUtil.DEFAULT_LB_POLICY;
1✔
172

173
  boolean fullStreamDecompression;
174

175
  DecompressorRegistry decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
1✔
176

177
  CompressorRegistry compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
1✔
178

179
  long idleTimeoutMillis = IDLE_MODE_DEFAULT_TIMEOUT_MILLIS;
1✔
180

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

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

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

194
  @Nullable
195
  BinaryLog binlog;
196

197
  @Nullable
198
  ProxyDetector proxyDetector;
199

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

878
        @Override
879
        public void start(Listener2 listener) {
880
          listener.onResult(
1✔
881
              ResolutionResult.newBuilder()
1✔
882
                  .setAddresses(Collections.singletonList(new EquivalentAddressGroup(address)))
1✔
883
                  .setAttributes(Attributes.EMPTY)
1✔
884
                  .build());
1✔
885
        }
1✔
886

887
        @Override
888
        public void shutdown() {}
1✔
889
      };
890
    }
891

892
    @Override
893
    public String getDefaultScheme() {
894
      return DIRECT_ADDRESS_SCHEME;
1✔
895
    }
896

897
    @Override
898
    protected boolean isAvailable() {
899
      return true;
1✔
900
    }
901

902
    @Override
903
    protected int priority() {
904
      return 5;
1✔
905
    }
906

907
    @Override
908
    public Collection<Class<? extends SocketAddress>> getProducedSocketAddressTypes() {
909
      return producedSocketAddressTypes;
1✔
910
    }
911
  }
912

913
  private static final class InterceptorFactoryWrapper implements ClientInterceptor {
914
    final InterceptorFactory factory;
915

916
    public InterceptorFactoryWrapper(InterceptorFactory factory) {
1✔
917
      this.factory = checkNotNull(factory, "factory");
1✔
918
    }
1✔
919

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

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

© 2025 Coveralls, Inc