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

grpc / grpc-java / #19600

20 Dec 2024 07:32AM UTC coverage: 88.609% (+0.009%) from 88.6%
#19600

push

github

web-flow
Introduce custom NameResolver.Args (#11669)

grpc-binder's upcoming AndroidIntentNameResolver needs to know the target Android user so it can resolve target URIs in the correct place. Unfortunately, Android's built in intent:// URI scheme has no way to specify a user and in fact the android.os.UserHandle object can't reasonably be encoded as a String at all.

We solve this problem by extending NameResolver.Args with the same type-safe and domain-specific Key<T> pattern used by CallOptions, Context and CreateSubchannelArgs. New "custom" arguments could apply to all NameResolvers of a certain URI scheme, to all NameResolvers producing a particular type of java.net.SocketAddress, or even to a specific NameResolver subclass.

33512 of 37820 relevant lines covered (88.61%)

0.89 hits per line

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

91.2
/../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.IdentityHashMap;
59
import java.util.LinkedHashMap;
60
import java.util.List;
61
import java.util.Map;
62
import java.util.concurrent.Executor;
63
import java.util.concurrent.TimeUnit;
64
import java.util.logging.Level;
65
import java.util.logging.Logger;
66
import java.util.regex.Pattern;
67
import javax.annotation.Nullable;
68

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

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

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

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

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

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

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

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

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

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

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

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

125
  private static final Method GET_CLIENT_INTERCEPTOR_METHOD;
126

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

148

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

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

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

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

158
  final String target;
159
  @Nullable
160
  final ChannelCredentials channelCredentials;
161
  @Nullable
162
  final CallCredentials callCredentials;
163
  @Nullable
164
  IdentityHashMap<NameResolver.Args.Key<?>, Object> nameResolverCustomArgs;
165

166
  @Nullable
167
  private final SocketAddress directServerAddress;
168

169
  @Nullable
170
  String userAgent;
171

172
  @Nullable
173
  String authorityOverride;
174

175
  String defaultLbPolicy = GrpcUtil.DEFAULT_LB_POLICY;
1✔
176

177
  boolean fullStreamDecompression;
178

179
  DecompressorRegistry decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
1✔
180

181
  CompressorRegistry compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
1✔
182

183
  long idleTimeoutMillis = IDLE_MODE_DEFAULT_TIMEOUT_MILLIS;
1✔
184

185
  int maxRetryAttempts = 5;
1✔
186
  int maxHedgedAttempts = 5;
1✔
187
  long retryBufferSize = DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES;
1✔
188
  long perRpcBufferLimit = DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES;
1✔
189
  boolean retryEnabled = true;
1✔
190

191
  InternalChannelz channelz = InternalChannelz.instance();
1✔
192
  int maxTraceEvents;
193

194
  @Nullable
195
  Map<String, ?> defaultServiceConfig;
196
  boolean lookUpServiceConfig = true;
1✔
197

198
  @Nullable
199
  BinaryLog binlog;
200

201
  @Nullable
202
  ProxyDetector proxyDetector;
203

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

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

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

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

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

247
    public FixedPortProvider(int port) {
1✔
248
      this.port = port;
1✔
249
    }
1✔
250

251
    @Override
252
    public int getDefaultPort() {
253
      return port;
1✔
254
    }
255
  }
256

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

265
  private final ClientTransportFactoryBuilder clientTransportFactoryBuilder;
266
  private final ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider;
267

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

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

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

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

322
  /**
323
   * Creates a new managed channel builder with the given server address, authority string of the
324
   * channel. Transport implementors must provide client transport factory builder, and may set
325
   * custom channel default port provider.
326
   */
327
  public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
328
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
329
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
330
    this(directServerAddress, authority, null, null, clientTransportFactoryBuilder,
1✔
331
        channelBuilderDefaultPortProvider);
332
  }
1✔
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
   * @param channelCreds The ChannelCredentials provided by the user. These may be used when
340
   *     creating derivative channels.
341
   */
342
  public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
343
      @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds,
344
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
345
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
1✔
346
    this.target = makeTargetStringForDirectAddress(directServerAddress);
1✔
347
    this.channelCredentials = channelCreds;
1✔
348
    this.callCredentials = callCreds;
1✔
349
    this.clientTransportFactoryBuilder = checkNotNull(clientTransportFactoryBuilder,
1✔
350
        "clientTransportFactoryBuilder");
351
    this.directServerAddress = directServerAddress;
1✔
352
    NameResolverRegistry reg = new NameResolverRegistry();
1✔
353
    reg.register(new DirectAddressNameResolverProvider(directServerAddress,
1✔
354
        authority));
355
    this.nameResolverRegistry = reg;
1✔
356

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

366
  @Override
367
  public ManagedChannelImplBuilder directExecutor() {
368
    return executor(MoreExecutors.directExecutor());
1✔
369
  }
370

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

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

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

397
  @Override
398
  public ManagedChannelImplBuilder intercept(ClientInterceptor... interceptors) {
399
    return intercept(Arrays.asList(interceptors));
1✔
400
  }
401

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

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

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

436
  ManagedChannelImplBuilder nameResolverRegistry(NameResolverRegistry resolverRegistry) {
437
    this.nameResolverRegistry = resolverRegistry;
1✔
438
    return this;
1✔
439
  }
440

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

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

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

471
  @Override
472
  public ManagedChannelImplBuilder userAgent(@Nullable String userAgent) {
473
    this.userAgent = userAgent;
1✔
474
    return this;
1✔
475
  }
476

477
  @Override
478
  public ManagedChannelImplBuilder overrideAuthority(String authority) {
479
    this.authorityOverride = checkAuthority(authority);
1✔
480
    return this;
1✔
481
  }
482

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

496
  @Override
497
  public ManagedChannelImplBuilder maxRetryAttempts(int maxRetryAttempts) {
498
    this.maxRetryAttempts = maxRetryAttempts;
1✔
499
    return this;
1✔
500
  }
501

502
  @Override
503
  public ManagedChannelImplBuilder maxHedgedAttempts(int maxHedgedAttempts) {
504
    this.maxHedgedAttempts = maxHedgedAttempts;
1✔
505
    return this;
1✔
506
  }
507

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

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

522
  @Override
523
  public ManagedChannelImplBuilder disableRetry() {
524
    retryEnabled = false;
1✔
525
    return this;
1✔
526
  }
527

528
  @Override
529
  public ManagedChannelImplBuilder enableRetry() {
530
    retryEnabled = true;
1✔
531
    return this;
1✔
532
  }
533

534
  @Override
535
  public ManagedChannelImplBuilder setBinaryLog(BinaryLog binlog) {
536
    this.binlog = binlog;
×
537
    return this;
×
538
  }
539

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

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

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

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

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

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

619
  @Override
620
  public <X> ManagedChannelImplBuilder setNameResolverArg(NameResolver.Args.Key<X> key, X value) {
621
    if (nameResolverCustomArgs == null) {
1✔
622
      nameResolverCustomArgs = new IdentityHashMap<>();
1✔
623
    }
624
    nameResolverCustomArgs.put(checkNotNull(key, "key"), checkNotNull(value, "value"));
1✔
625
    return this;
1✔
626
  }
627

628
  @SuppressWarnings("unchecked") // This cast is safe because of setNameResolverArg()'s signature.
629
  void copyAllNameResolverCustomArgsTo(NameResolver.Args.Builder dest) {
630
    if (nameResolverCustomArgs != null) {
1✔
631
      for (Map.Entry<NameResolver.Args.Key<?>, Object> entry : nameResolverCustomArgs.entrySet()) {
1✔
632
        dest.setArg((NameResolver.Args.Key<Object>) entry.getKey(), entry.getValue());
1✔
633
      }
1✔
634
    }
635
  }
1✔
636

637
  @Override
638
  public ManagedChannelImplBuilder disableServiceConfigLookUp() {
639
    this.lookUpServiceConfig = false;
1✔
640
    return this;
1✔
641
  }
642

643
  /**
644
   * Disable or enable stats features. Enabled by default.
645
   *
646
   * <p>For the current release, calling {@code setStatsEnabled(true)} may have a side effect that
647
   * disables retry.
648
   */
649
  public void setStatsEnabled(boolean value) {
650
    statsEnabled = value;
1✔
651
  }
1✔
652

653
  /**
654
   * Disable or enable stats recording for RPC upstarts.  Effective only if {@link
655
   * #setStatsEnabled} is set to true.  Enabled by default.
656
   */
657
  public void setStatsRecordStartedRpcs(boolean value) {
658
    recordStartedRpcs = value;
1✔
659
  }
1✔
660

661
  /**
662
   * Disable or enable stats recording for RPC completions.  Effective only if {@link
663
   * #setStatsEnabled} is set to true.  Enabled by default.
664
   */
665
  public void setStatsRecordFinishedRpcs(boolean value) {
666
    recordFinishedRpcs = value;
1✔
667
  }
1✔
668

669
  /**
670
   * Disable or enable real-time metrics recording.  Effective only if {@link #setStatsEnabled} is
671
   * set to true.  Disabled by default.
672
   */
673
  public void setStatsRecordRealTimeMetrics(boolean value) {
674
    recordRealTimeMetrics = value;
×
675
  }
×
676
  
677
  public void setStatsRecordRetryMetrics(boolean value) {
678
    recordRetryMetrics = value;
1✔
679
  }
1✔
680

681
  /**
682
   * Disable or enable tracing features.  Enabled by default.
683
   */
684
  public void setTracingEnabled(boolean value) {
685
    tracingEnabled = value;
1✔
686
  }
1✔
687

688
  /**
689
   * Verifies the authority is valid.
690
   */
691
  @VisibleForTesting
692
  String checkAuthority(String authority) {
693
    if (authorityCheckerDisabled) {
1✔
694
      return authority;
1✔
695
    }
696
    return GrpcUtil.checkAuthority(authority);
1✔
697
  }
698

699
  /** Disable the check whether the authority is valid. */
700
  public ManagedChannelImplBuilder disableCheckAuthority() {
701
    authorityCheckerDisabled = true;
1✔
702
    return this;
1✔
703
  }
704

705
  /** Enable previously disabled authority check. */
706
  public ManagedChannelImplBuilder enableCheckAuthority() {
707
    authorityCheckerDisabled = false;
1✔
708
    return this;
1✔
709
  }
710

711
  @Override
712
  protected ManagedChannelImplBuilder addMetricSink(MetricSink metricSink) {
713
    metricSinks.add(checkNotNull(metricSink, "metric sink"));
1✔
714
    return this;
1✔
715
  }
716

717
  @Override
718
  public ManagedChannel build() {
719
    ClientTransportFactory clientTransportFactory =
1✔
720
        clientTransportFactoryBuilder.buildClientTransportFactory();
1✔
721
    ResolvedNameResolver resolvedResolver = getNameResolverProvider(
1✔
722
        target, nameResolverRegistry, clientTransportFactory.getSupportedSocketAddressTypes());
1✔
723
    return new ManagedChannelOrphanWrapper(new ManagedChannelImpl(
1✔
724
        this,
725
        clientTransportFactory,
726
        resolvedResolver.targetUri,
727
        resolvedResolver.provider,
728
        new ExponentialBackoffPolicy.Provider(),
729
        SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR),
1✔
730
        GrpcUtil.STOPWATCH_SUPPLIER,
731
        getEffectiveInterceptors(resolvedResolver.targetUri.toString()),
1✔
732
        TimeProvider.SYSTEM_TIME_PROVIDER));
733
  }
734

735
  // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know
736
  // what should be the desired behavior for retry + stats/tracing.
737
  // TODO(zdapeng): FIX IT
738
  @VisibleForTesting
739
  List<ClientInterceptor> getEffectiveInterceptors(String computedTarget) {
740
    List<ClientInterceptor> effectiveInterceptors = new ArrayList<>(this.interceptors);
1✔
741
    for (int i = 0; i < effectiveInterceptors.size(); i++) {
1✔
742
      if (!(effectiveInterceptors.get(i) instanceof InterceptorFactoryWrapper)) {
1✔
743
        continue;
1✔
744
      }
745
      InterceptorFactory factory =
1✔
746
          ((InterceptorFactoryWrapper) effectiveInterceptors.get(i)).factory;
1✔
747
      ClientInterceptor interceptor = factory.newInterceptor(computedTarget);
1✔
748
      if (interceptor == null) {
1✔
749
        throw new NullPointerException("Factory returned null interceptor: " + factory);
×
750
      }
751
      effectiveInterceptors.set(i, interceptor);
1✔
752
    }
753

754
    boolean disableImplicitCensus = InternalConfiguratorRegistry.wasSetConfiguratorsCalled();
1✔
755
    if (disableImplicitCensus) {
1✔
756
      return effectiveInterceptors;
×
757
    }
758
    if (statsEnabled) {
1✔
759
      ClientInterceptor statsInterceptor = null;
1✔
760

761
      if (GET_CLIENT_INTERCEPTOR_METHOD != null) {
1✔
762
        try {
763
          statsInterceptor =
1✔
764
            (ClientInterceptor) GET_CLIENT_INTERCEPTOR_METHOD
765
              .invoke(
1✔
766
                null,
767
                recordStartedRpcs,
1✔
768
                recordFinishedRpcs,
1✔
769
                recordRealTimeMetrics,
1✔
770
                recordRetryMetrics);
1✔
771
        } catch (IllegalAccessException e) {
×
772
          log.log(Level.FINE, "Unable to apply census stats", e);
×
773
        } catch (InvocationTargetException e) {
×
774
          log.log(Level.FINE, "Unable to apply census stats", e);
×
775
        }
1✔
776
      }
777

778
      if (statsInterceptor != null) {
1✔
779
        // First interceptor runs last (see ClientInterceptors.intercept()), so that no
780
        // other interceptor can override the tracer factory we set in CallOptions.
781
        effectiveInterceptors.add(0, statsInterceptor);
1✔
782
      }
783
    }
784
    if (tracingEnabled) {
1✔
785
      ClientInterceptor tracingInterceptor = null;
1✔
786
      try {
787
        Class<?> censusTracingAccessor =
1✔
788
            Class.forName("io.grpc.census.InternalCensusTracingAccessor");
1✔
789
        Method getClientInterceptroMethod =
1✔
790
            censusTracingAccessor.getDeclaredMethod("getClientInterceptor");
1✔
791
        tracingInterceptor = (ClientInterceptor) getClientInterceptroMethod.invoke(null);
1✔
792
      } catch (ClassNotFoundException e) {
1✔
793
        // Replace these separate catch statements with multicatch when Android min-API >= 19
794
        log.log(Level.FINE, "Unable to apply census stats", e);
1✔
795
      } catch (NoSuchMethodException e) {
×
796
        log.log(Level.FINE, "Unable to apply census stats", e);
×
797
      } catch (IllegalAccessException e) {
×
798
        log.log(Level.FINE, "Unable to apply census stats", e);
×
799
      } catch (InvocationTargetException e) {
×
800
        log.log(Level.FINE, "Unable to apply census stats", e);
×
801
      }
1✔
802
      if (tracingInterceptor != null) {
1✔
803
        effectiveInterceptors.add(0, tracingInterceptor);
1✔
804
      }
805
    }
806
    return effectiveInterceptors;
1✔
807
  }
808

809
  /**
810
   * Returns a default port to {@link NameResolver} for use in cases where the target string doesn't
811
   * include a port. The default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}.
812
   */
813
  int getDefaultPort() {
814
    return channelBuilderDefaultPortProvider.getDefaultPort();
1✔
815
  }
816

817
  @VisibleForTesting
818
  static class ResolvedNameResolver {
819
    public final URI targetUri;
820
    public final NameResolverProvider provider;
821

822
    public ResolvedNameResolver(URI targetUri, NameResolverProvider provider) {
1✔
823
      this.targetUri = checkNotNull(targetUri, "targetUri");
1✔
824
      this.provider = checkNotNull(provider, "provider");
1✔
825
    }
1✔
826
  }
827

828
  @VisibleForTesting
829
  static ResolvedNameResolver getNameResolverProvider(
830
      String target, NameResolverRegistry nameResolverRegistry,
831
      Collection<Class<? extends SocketAddress>> channelTransportSocketAddressTypes) {
832
    // Finding a NameResolver. Try using the target string as the URI. If that fails, try prepending
833
    // "dns:///".
834
    NameResolverProvider provider = null;
1✔
835
    URI targetUri = null;
1✔
836
    StringBuilder uriSyntaxErrors = new StringBuilder();
1✔
837
    try {
838
      targetUri = new URI(target);
1✔
839
    } catch (URISyntaxException e) {
1✔
840
      // Can happen with ip addresses like "[::1]:1234" or 127.0.0.1:1234.
841
      uriSyntaxErrors.append(e.getMessage());
1✔
842
    }
1✔
843
    if (targetUri != null) {
1✔
844
      // For "localhost:8080" this would likely cause provider to be null, because "localhost" is
845
      // parsed as the scheme. Will hit the next case and try "dns:///localhost:8080".
846
      provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
847
    }
848

849
    if (provider == null && !URI_PATTERN.matcher(target).matches()) {
1✔
850
      // It doesn't look like a URI target. Maybe it's an authority string. Try with the default
851
      // scheme from the registry.
852
      try {
853
        targetUri = new URI(nameResolverRegistry.getDefaultScheme(), "", "/" + target, null);
1✔
854
      } catch (URISyntaxException e) {
×
855
        // Should not be possible.
856
        throw new IllegalArgumentException(e);
×
857
      }
1✔
858
      provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
1✔
859
    }
860

861
    if (provider == null) {
1✔
862
      throw new IllegalArgumentException(String.format(
1✔
863
          "Could not find a NameResolverProvider for %s%s",
864
          target, uriSyntaxErrors.length() > 0 ? " (" + uriSyntaxErrors + ")" : ""));
1✔
865
    }
866

867
    if (channelTransportSocketAddressTypes != null) {
1✔
868
      Collection<Class<? extends SocketAddress>> nameResolverSocketAddressTypes
1✔
869
          = provider.getProducedSocketAddressTypes();
1✔
870
      if (!channelTransportSocketAddressTypes.containsAll(nameResolverSocketAddressTypes)) {
1✔
871
        throw new IllegalArgumentException(String.format(
1✔
872
            "Address types of NameResolver '%s' for '%s' not supported by transport",
873
            targetUri.getScheme(), target));
1✔
874
      }
875
    }
876

877
    return new ResolvedNameResolver(targetUri, provider);
1✔
878
  }
879

880
  private static class DirectAddressNameResolverProvider extends NameResolverProvider {
881
    final SocketAddress address;
882
    final String authority;
883
    final Collection<Class<? extends SocketAddress>> producedSocketAddressTypes;
884

885
    DirectAddressNameResolverProvider(SocketAddress address, String authority) {
1✔
886
      this.address = address;
1✔
887
      this.authority = authority;
1✔
888
      this.producedSocketAddressTypes
1✔
889
          = Collections.singleton(address.getClass());
1✔
890
    }
1✔
891

892
    @Override
893
    public NameResolver newNameResolver(URI notUsedUri, NameResolver.Args args) {
894
      return new NameResolver() {
1✔
895
        @Override
896
        public String getServiceAuthority() {
897
          return authority;
1✔
898
        }
899

900
        @Override
901
        public void start(Listener2 listener) {
902
          listener.onResult2(
1✔
903
              ResolutionResult.newBuilder()
1✔
904
                  .setAddressesOrError(
1✔
905
                      StatusOr.fromValue(
1✔
906
                          Collections.singletonList(new EquivalentAddressGroup(address))))
1✔
907
                  .setAttributes(Attributes.EMPTY)
1✔
908
                  .build());
1✔
909
        }
1✔
910

911
        @Override
912
        public void shutdown() {}
1✔
913
      };
914
    }
915

916
    @Override
917
    public String getDefaultScheme() {
918
      return DIRECT_ADDRESS_SCHEME;
1✔
919
    }
920

921
    @Override
922
    protected boolean isAvailable() {
923
      return true;
1✔
924
    }
925

926
    @Override
927
    protected int priority() {
928
      return 5;
1✔
929
    }
930

931
    @Override
932
    public Collection<Class<? extends SocketAddress>> getProducedSocketAddressTypes() {
933
      return producedSocketAddressTypes;
1✔
934
    }
935
  }
936

937
  private static final class InterceptorFactoryWrapper implements ClientInterceptor {
938
    final InterceptorFactory factory;
939

940
    public InterceptorFactoryWrapper(InterceptorFactory factory) {
1✔
941
      this.factory = checkNotNull(factory, "factory");
1✔
942
    }
1✔
943

944
    @Override
945
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
946
        MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
947
      throw new AssertionError("Should have been replaced with real instance");
×
948
    }
949
  }
950

951
  /**
952
   * Returns the internal offload executor pool for offloading tasks.
953
   */
954
  public ObjectPool<? extends Executor> getOffloadExecutorPool() {
955
    return this.offloadExecutorPool;
1✔
956
  }
957
}
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