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

grpc / grpc-java / #19211

08 May 2024 10:50PM UTC coverage: 88.309% (-0.02%) from 88.328%
#19211

push

github

ejona86
xds: Plumb locality in xds_cluster_impl and weighted_target

As part of gRFC A78:

> To support the locality label in the WRR metrics, we will extend the
> `weighted_target` LB policy (see A28) to define a resolver attribute
> that indicates the name of its child. This attribute will be passed
> down to each of its children with the appropriate value, so that any
> LB policy that sits underneath the `weighted_target` policy will be
> able to use it.

xds_cluster_impl is involved because it uses the child names in the
AddressFilter, which must match the names used by weighted_target.
Instead of using Locality.toString() in multiple policies and assuming
the policies agree, we now have xds_cluster_impl decide the locality's
name and pass it down explicitly. This allows us to change the name
format to match gRFC A78:

> If locality information is available, the value of this label will be
> of the form `{region="${REGION}", zone="${ZONE}",
> sub_zone="${SUB_ZONE}"}`, where `${REGION}`, `${ZONE}`, and
> `${SUB_ZONE}` are replaced with the actual values. If no locality
> information is available, the label will be set to the empty string.

31515 of 35687 relevant lines covered (88.31%)

0.88 hits per line

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

89.47
/../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.ChannelCredentials;
30
import io.grpc.ClientInterceptor;
31
import io.grpc.ClientTransportFilter;
32
import io.grpc.CompressorRegistry;
33
import io.grpc.Configurator;
34
import io.grpc.ConfiguratorRegistry;
35
import io.grpc.DecompressorRegistry;
36
import io.grpc.EquivalentAddressGroup;
37
import io.grpc.InternalChannelz;
38
import io.grpc.InternalGlobalInterceptors;
39
import io.grpc.ManagedChannel;
40
import io.grpc.ManagedChannelBuilder;
41
import io.grpc.MetricSink;
42
import io.grpc.NameResolver;
43
import io.grpc.NameResolverProvider;
44
import io.grpc.NameResolverRegistry;
45
import io.grpc.ProxyDetector;
46
import java.lang.reflect.InvocationTargetException;
47
import java.lang.reflect.Method;
48
import java.net.SocketAddress;
49
import java.net.URI;
50
import java.net.URISyntaxException;
51
import java.util.ArrayList;
52
import java.util.Arrays;
53
import java.util.Collection;
54
import java.util.Collections;
55
import java.util.LinkedHashMap;
56
import java.util.List;
57
import java.util.Map;
58
import java.util.concurrent.Executor;
59
import java.util.concurrent.TimeUnit;
60
import java.util.logging.Level;
61
import java.util.logging.Logger;
62
import javax.annotation.Nullable;
63

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

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

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

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

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

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

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

102
  private static final ObjectPool<? extends Executor> DEFAULT_EXECUTOR_POOL =
1✔
103
      SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR);
1✔
104

105
  private static final DecompressorRegistry DEFAULT_DECOMPRESSOR_REGISTRY =
106
      DecompressorRegistry.getDefaultInstance();
1✔
107

108
  private static final CompressorRegistry DEFAULT_COMPRESSOR_REGISTRY =
109
      CompressorRegistry.getDefaultInstance();
1✔
110

111
  private static final long DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES = 1L << 24;  // 16M
112
  private static final long DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES = 1L << 20; // 1M
113

114
  private static final Method GET_CLIENT_INTERCEPTOR_METHOD;
115

116
  static {
117
    Method getClientInterceptorMethod = null;
1✔
118
    try {
119
      Class<?> censusStatsAccessor =
1✔
120
          Class.forName("io.grpc.census.InternalCensusStatsAccessor");
1✔
121
      getClientInterceptorMethod =
1✔
122
          censusStatsAccessor.getDeclaredMethod(
1✔
123
              "getClientInterceptor",
124
              boolean.class,
125
              boolean.class,
126
              boolean.class,
127
              boolean.class);
128
    } catch (ClassNotFoundException e) {
1✔
129
      // Replace these separate catch statements with multicatch when Android min-API >= 19
130
      log.log(Level.FINE, "Unable to apply census stats", e);
1✔
131
    } catch (NoSuchMethodException e) {
×
132
      log.log(Level.FINE, "Unable to apply census stats", e);
×
133
    }
1✔
134
    GET_CLIENT_INTERCEPTOR_METHOD = getClientInterceptorMethod;
1✔
135
  }
1✔
136

137

138
  ObjectPool<? extends Executor> executorPool = DEFAULT_EXECUTOR_POOL;
1✔
139

140
  ObjectPool<? extends Executor> offloadExecutorPool = DEFAULT_EXECUTOR_POOL;
1✔
141

142
  private final List<ClientInterceptor> interceptors = new ArrayList<>();
1✔
143
  NameResolverRegistry nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
1✔
144

145
  final List<ClientTransportFilter> transportFilters = new ArrayList<>();
1✔
146

147
  final String target;
148
  @Nullable
149
  final ChannelCredentials channelCredentials;
150
  @Nullable
151
  final CallCredentials callCredentials;
152

153
  @Nullable
154
  private final SocketAddress directServerAddress;
155

156
  @Nullable
157
  String userAgent;
158

159
  @Nullable
160
  String authorityOverride;
161

162
  String defaultLbPolicy = GrpcUtil.DEFAULT_LB_POLICY;
1✔
163

164
  boolean fullStreamDecompression;
165

166
  DecompressorRegistry decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
1✔
167

168
  CompressorRegistry compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
1✔
169

170
  long idleTimeoutMillis = IDLE_MODE_DEFAULT_TIMEOUT_MILLIS;
1✔
171

172
  int maxRetryAttempts = 5;
1✔
173
  int maxHedgedAttempts = 5;
1✔
174
  long retryBufferSize = DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES;
1✔
175
  long perRpcBufferLimit = DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES;
1✔
176
  boolean retryEnabled = true;
1✔
177

178
  InternalChannelz channelz = InternalChannelz.instance();
1✔
179
  int maxTraceEvents;
180

181
  @Nullable
182
  Map<String, ?> defaultServiceConfig;
183
  boolean lookUpServiceConfig = true;
1✔
184

185
  @Nullable
186
  BinaryLog binlog;
187

188
  @Nullable
189
  ProxyDetector proxyDetector;
190

191
  private boolean authorityCheckerDisabled;
192
  private boolean statsEnabled = true;
1✔
193
  private boolean recordStartedRpcs = true;
1✔
194
  private boolean recordFinishedRpcs = true;
1✔
195
  private boolean recordRealTimeMetrics = false;
1✔
196
  private boolean recordRetryMetrics = true;
1✔
197
  private boolean tracingEnabled = true;
1✔
198
  List<MetricSink> metricSinks = new ArrayList<>();
1✔
199

200
  /**
201
   * An interface for Transport implementors to provide the {@link ClientTransportFactory}
202
   * appropriate for the channel.
203
   */
204
  public interface ClientTransportFactoryBuilder {
205
    ClientTransportFactory buildClientTransportFactory();
206
  }
207

208
  /**
209
   * Convenience ClientTransportFactoryBuilder, throws UnsupportedOperationException().
210
   */
211
  public static class UnsupportedClientTransportFactoryBuilder implements
1✔
212
      ClientTransportFactoryBuilder {
213
    @Override
214
    public ClientTransportFactory buildClientTransportFactory() {
215
      throw new UnsupportedOperationException();
×
216
    }
217
  }
218

219
  /**
220
   * An interface for Transport implementors to provide a default port to {@link
221
   * io.grpc.NameResolver} for use in cases where the target string doesn't include a port. The
222
   * default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}.
223
   */
224
  public interface ChannelBuilderDefaultPortProvider {
225
    int getDefaultPort();
226
  }
227

228
  /**
229
   * Default implementation of {@link ChannelBuilderDefaultPortProvider} that returns a fixed port.
230
   */
231
  public static final class FixedPortProvider implements ChannelBuilderDefaultPortProvider {
232
    private final int port;
233

234
    public FixedPortProvider(int port) {
1✔
235
      this.port = port;
1✔
236
    }
1✔
237

238
    @Override
239
    public int getDefaultPort() {
240
      return port;
1✔
241
    }
242
  }
243

244
  private static final class ManagedChannelDefaultPortProvider implements
245
      ChannelBuilderDefaultPortProvider {
246
    @Override
247
    public int getDefaultPort() {
248
      return GrpcUtil.DEFAULT_PORT_SSL;
1✔
249
    }
250
  }
251

252
  private final ClientTransportFactoryBuilder clientTransportFactoryBuilder;
253
  private final ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider;
254

255
  /**
256
   * Creates a new managed channel builder with a target string, which can be either a valid {@link
257
   * io.grpc.NameResolver}-compliant URI, or an authority string. Transport implementors must
258
   * provide client transport factory builder, and may set custom channel default port provider.
259
   */
260
  public ManagedChannelImplBuilder(String target,
261
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
262
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
263
    this(target, null, null, clientTransportFactoryBuilder, channelBuilderDefaultPortProvider);
1✔
264
  }
1✔
265

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

285
    if (channelBuilderDefaultPortProvider != null) {
1✔
286
      this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider;
1✔
287
    } else {
288
      this.channelBuilderDefaultPortProvider = new ManagedChannelDefaultPortProvider();
1✔
289
    }
290
  }
1✔
291

292
  /**
293
   * Returns a target string for the SocketAddress. It is only used as a placeholder, because
294
   * DirectAddressNameResolverProvider will not actually try to use it. However, it must be a valid
295
   * URI.
296
   */
297
  @VisibleForTesting
298
  static String makeTargetStringForDirectAddress(SocketAddress address) {
299
    try {
300
      return new URI(DIRECT_ADDRESS_SCHEME, "", "/" + address, null).toString();
1✔
301
    } catch (URISyntaxException e) {
×
302
      // It should not happen.
303
      throw new RuntimeException(e);
×
304
    }
305
  }
306

307
  /**
308
   * Creates a new managed channel builder with the given server address, authority string of the
309
   * channel. Transport implementors must provide client transport factory builder, and may set
310
   * custom channel default port provider.
311
   */
312
  public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
313
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
314
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
315
    this(directServerAddress, authority, null, null, clientTransportFactoryBuilder,
1✔
316
        channelBuilderDefaultPortProvider);
317
  }
1✔
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
   * @param channelCreds The ChannelCredentials provided by the user. These may be used when
325
   *     creating derivative channels.
326
   */
327
  public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
328
      @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds,
329
      ClientTransportFactoryBuilder clientTransportFactoryBuilder,
330
      @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
1✔
331
    this.target = makeTargetStringForDirectAddress(directServerAddress);
1✔
332
    this.channelCredentials = channelCreds;
1✔
333
    this.callCredentials = callCreds;
1✔
334
    this.clientTransportFactoryBuilder = checkNotNull(clientTransportFactoryBuilder,
1✔
335
        "clientTransportFactoryBuilder");
336
    this.directServerAddress = directServerAddress;
1✔
337
    NameResolverRegistry reg = new NameResolverRegistry();
1✔
338
    reg.register(new DirectAddressNameResolverProvider(directServerAddress,
1✔
339
        authority));
340
    this.nameResolverRegistry = reg;
1✔
341

342
    if (channelBuilderDefaultPortProvider != null) {
1✔
343
      this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider;
1✔
344
    } else {
345
      this.channelBuilderDefaultPortProvider = new ManagedChannelDefaultPortProvider();
1✔
346
    }
347
    // TODO(dnvindhya): Move configurator to all the individual builders
348
    for (Configurator configurator : ConfiguratorRegistry.getDefaultRegistry().getConfigurators()) {
1✔
349
      configurator.configureChannelBuilder(this);
×
350
    }
×
351
  }
1✔
352

353
  @Override
354
  public ManagedChannelImplBuilder directExecutor() {
355
    return executor(MoreExecutors.directExecutor());
1✔
356
  }
357

358
  @Override
359
  public ManagedChannelImplBuilder executor(Executor executor) {
360
    if (executor != null) {
1✔
361
      this.executorPool = new FixedObjectPool<>(executor);
1✔
362
    } else {
363
      this.executorPool = DEFAULT_EXECUTOR_POOL;
1✔
364
    }
365
    return this;
1✔
366
  }
367

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

378
  @Override
379
  public ManagedChannelImplBuilder intercept(List<ClientInterceptor> interceptors) {
380
    this.interceptors.addAll(interceptors);
1✔
381
    return this;
1✔
382
  }
383

384
  @Override
385
  public ManagedChannelImplBuilder intercept(ClientInterceptor... interceptors) {
386
    return intercept(Arrays.asList(interceptors));
1✔
387
  }
388

389
  @Override
390
  public ManagedChannelImplBuilder addTransportFilter(ClientTransportFilter hook) {
391
    transportFilters.add(checkNotNull(hook, "transport filter"));
1✔
392
    return this;
1✔
393
  }
394

395
  @Deprecated
396
  @Override
397
  public ManagedChannelImplBuilder nameResolverFactory(NameResolver.Factory resolverFactory) {
398
    Preconditions.checkState(directServerAddress == null,
1✔
399
        "directServerAddress is set (%s), which forbids the use of NameResolverFactory",
400
        directServerAddress);
401
    if (resolverFactory != null) {
1✔
402
      NameResolverRegistry reg = new NameResolverRegistry();
1✔
403
      if (resolverFactory instanceof NameResolverProvider) {
1✔
404
        reg.register((NameResolverProvider) resolverFactory);
×
405
      } else {
406
        reg.register(new NameResolverFactoryToProviderFacade(resolverFactory));
1✔
407
      }
408
      this.nameResolverRegistry = reg;
1✔
409
    } else {
1✔
410
      this.nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
1✔
411
    }
412
    return this;
1✔
413
  }
414

415
  ManagedChannelImplBuilder nameResolverRegistry(NameResolverRegistry resolverRegistry) {
416
    this.nameResolverRegistry = resolverRegistry;
1✔
417
    return this;
1✔
418
  }
419

420
  @Override
421
  public ManagedChannelImplBuilder defaultLoadBalancingPolicy(String policy) {
422
    Preconditions.checkState(directServerAddress == null,
1✔
423
        "directServerAddress is set (%s), which forbids the use of load-balancing policy",
424
        directServerAddress);
425
    Preconditions.checkArgument(policy != null, "policy cannot be null");
1✔
426
    this.defaultLbPolicy = policy;
1✔
427
    return this;
1✔
428
  }
429

430
  @Override
431
  public ManagedChannelImplBuilder decompressorRegistry(DecompressorRegistry registry) {
432
    if (registry != null) {
1✔
433
      this.decompressorRegistry = registry;
1✔
434
    } else {
435
      this.decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
1✔
436
    }
437
    return this;
1✔
438
  }
439

440
  @Override
441
  public ManagedChannelImplBuilder compressorRegistry(CompressorRegistry registry) {
442
    if (registry != null) {
1✔
443
      this.compressorRegistry = registry;
1✔
444
    } else {
445
      this.compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
1✔
446
    }
447
    return this;
1✔
448
  }
449

450
  @Override
451
  public ManagedChannelImplBuilder userAgent(@Nullable String userAgent) {
452
    this.userAgent = userAgent;
1✔
453
    return this;
1✔
454
  }
455

456
  @Override
457
  public ManagedChannelImplBuilder overrideAuthority(String authority) {
458
    this.authorityOverride = checkAuthority(authority);
1✔
459
    return this;
1✔
460
  }
461

462
  @Override
463
  public ManagedChannelImplBuilder idleTimeout(long value, TimeUnit unit) {
464
    checkArgument(value > 0, "idle timeout is %s, but must be positive", value);
1✔
465
    // We convert to the largest unit to avoid overflow
466
    if (unit.toDays(value) >= IDLE_MODE_MAX_TIMEOUT_DAYS) {
1✔
467
      // This disables idle mode
468
      this.idleTimeoutMillis = ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE;
1✔
469
    } else {
470
      this.idleTimeoutMillis = Math.max(unit.toMillis(value), IDLE_MODE_MIN_TIMEOUT_MILLIS);
1✔
471
    }
472
    return this;
1✔
473
  }
474

475
  @Override
476
  public ManagedChannelImplBuilder maxRetryAttempts(int maxRetryAttempts) {
477
    this.maxRetryAttempts = maxRetryAttempts;
1✔
478
    return this;
1✔
479
  }
480

481
  @Override
482
  public ManagedChannelImplBuilder maxHedgedAttempts(int maxHedgedAttempts) {
483
    this.maxHedgedAttempts = maxHedgedAttempts;
1✔
484
    return this;
1✔
485
  }
486

487
  @Override
488
  public ManagedChannelImplBuilder retryBufferSize(long bytes) {
489
    checkArgument(bytes > 0L, "retry buffer size must be positive");
1✔
490
    retryBufferSize = bytes;
1✔
491
    return this;
1✔
492
  }
493

494
  @Override
495
  public ManagedChannelImplBuilder perRpcBufferLimit(long bytes) {
496
    checkArgument(bytes > 0L, "per RPC buffer limit must be positive");
1✔
497
    perRpcBufferLimit = bytes;
1✔
498
    return this;
1✔
499
  }
500

501
  @Override
502
  public ManagedChannelImplBuilder disableRetry() {
503
    retryEnabled = false;
1✔
504
    return this;
1✔
505
  }
506

507
  @Override
508
  public ManagedChannelImplBuilder enableRetry() {
509
    retryEnabled = true;
1✔
510
    return this;
1✔
511
  }
512

513
  @Override
514
  public ManagedChannelImplBuilder setBinaryLog(BinaryLog binlog) {
515
    this.binlog = binlog;
×
516
    return this;
×
517
  }
518

519
  @Override
520
  public ManagedChannelImplBuilder maxTraceEvents(int maxTraceEvents) {
521
    checkArgument(maxTraceEvents >= 0, "maxTraceEvents must be non-negative");
1✔
522
    this.maxTraceEvents = maxTraceEvents;
1✔
523
    return this;
1✔
524
  }
525

526
  @Override
527
  public ManagedChannelImplBuilder proxyDetector(@Nullable ProxyDetector proxyDetector) {
528
    this.proxyDetector = proxyDetector;
1✔
529
    return this;
1✔
530
  }
531

532
  @Override
533
  public ManagedChannelImplBuilder defaultServiceConfig(@Nullable Map<String, ?> serviceConfig) {
534
    // TODO(notcarl): use real parsing
535
    defaultServiceConfig = checkMapEntryTypes(serviceConfig);
1✔
536
    return this;
1✔
537
  }
538

539
  @Nullable
540
  private static Map<String, ?> checkMapEntryTypes(@Nullable Map<?, ?> map) {
541
    if (map == null) {
1✔
542
      return null;
×
543
    }
544
    // Not using ImmutableMap.Builder because of extra guava dependency for Android.
545
    Map<String, Object> parsedMap = new LinkedHashMap<>();
1✔
546
    for (Map.Entry<?, ?> entry : map.entrySet()) {
1✔
547
      checkArgument(
1✔
548
          entry.getKey() instanceof String,
1✔
549
          "The key of the entry '%s' is not of String type", entry);
550

551
      String key = (String) entry.getKey();
1✔
552
      Object value = entry.getValue();
1✔
553
      if (value == null) {
1✔
554
        parsedMap.put(key, null);
1✔
555
      } else if (value instanceof Map) {
1✔
556
        parsedMap.put(key, checkMapEntryTypes((Map<?, ?>) value));
1✔
557
      } else if (value instanceof List) {
1✔
558
        parsedMap.put(key, checkListEntryTypes((List<?>) value));
1✔
559
      } else if (value instanceof String) {
1✔
560
        parsedMap.put(key, value);
1✔
561
      } else if (value instanceof Double) {
1✔
562
        parsedMap.put(key, value);
1✔
563
      } else if (value instanceof Boolean) {
1✔
564
        parsedMap.put(key, value);
1✔
565
      } else {
566
        throw new IllegalArgumentException(
1✔
567
            "The value of the map entry '" + entry + "' is of type '" + value.getClass()
1✔
568
                + "', which is not supported");
569
      }
570
    }
1✔
571
    return Collections.unmodifiableMap(parsedMap);
1✔
572
  }
573

574
  private static List<?> checkListEntryTypes(List<?> list) {
575
    List<Object> parsedList = new ArrayList<>(list.size());
1✔
576
    for (Object value : list) {
1✔
577
      if (value == null) {
1✔
578
        parsedList.add(null);
1✔
579
      } else if (value instanceof Map) {
1✔
580
        parsedList.add(checkMapEntryTypes((Map<?, ?>) value));
1✔
581
      } else if (value instanceof List) {
1✔
582
        parsedList.add(checkListEntryTypes((List<?>) value));
×
583
      } else if (value instanceof String) {
1✔
584
        parsedList.add(value);
1✔
585
      } else if (value instanceof Double) {
1✔
586
        parsedList.add(value);
1✔
587
      } else if (value instanceof Boolean) {
1✔
588
        parsedList.add(value);
1✔
589
      } else {
590
        throw new IllegalArgumentException(
×
591
            "The entry '" + value + "' is of type '" + value.getClass()
×
592
                + "', which is not supported");
593
      }
594
    }
1✔
595
    return Collections.unmodifiableList(parsedList);
1✔
596
  }
597

598
  @Override
599
  public ManagedChannelImplBuilder disableServiceConfigLookUp() {
600
    this.lookUpServiceConfig = false;
1✔
601
    return this;
1✔
602
  }
603

604
  /**
605
   * Disable or enable stats features. Enabled by default.
606
   *
607
   * <p>For the current release, calling {@code setStatsEnabled(true)} may have a side effect that
608
   * disables retry.
609
   */
610
  public void setStatsEnabled(boolean value) {
611
    statsEnabled = value;
1✔
612
  }
1✔
613

614
  /**
615
   * Disable or enable stats recording for RPC upstarts.  Effective only if {@link
616
   * #setStatsEnabled} is set to true.  Enabled by default.
617
   */
618
  public void setStatsRecordStartedRpcs(boolean value) {
619
    recordStartedRpcs = value;
1✔
620
  }
1✔
621

622
  /**
623
   * Disable or enable stats recording for RPC completions.  Effective only if {@link
624
   * #setStatsEnabled} is set to true.  Enabled by default.
625
   */
626
  public void setStatsRecordFinishedRpcs(boolean value) {
627
    recordFinishedRpcs = value;
1✔
628
  }
1✔
629

630
  /**
631
   * Disable or enable real-time metrics recording.  Effective only if {@link #setStatsEnabled} is
632
   * set to true.  Disabled by default.
633
   */
634
  public void setStatsRecordRealTimeMetrics(boolean value) {
635
    recordRealTimeMetrics = value;
×
636
  }
×
637
  
638
  public void setStatsRecordRetryMetrics(boolean value) {
639
    recordRetryMetrics = value;
1✔
640
  }
1✔
641

642
  /**
643
   * Disable or enable tracing features.  Enabled by default.
644
   */
645
  public void setTracingEnabled(boolean value) {
646
    tracingEnabled = value;
1✔
647
  }
1✔
648

649
  /**
650
   * Verifies the authority is valid.
651
   */
652
  @VisibleForTesting
653
  String checkAuthority(String authority) {
654
    if (authorityCheckerDisabled) {
1✔
655
      return authority;
1✔
656
    }
657
    return GrpcUtil.checkAuthority(authority);
1✔
658
  }
659

660
  /** Disable the check whether the authority is valid. */
661
  public ManagedChannelImplBuilder disableCheckAuthority() {
662
    authorityCheckerDisabled = true;
1✔
663
    return this;
1✔
664
  }
665

666
  /** Enable previously disabled authority check. */
667
  public ManagedChannelImplBuilder enableCheckAuthority() {
668
    authorityCheckerDisabled = false;
1✔
669
    return this;
1✔
670
  }
671

672
  @Override
673
  public ManagedChannelImplBuilder addMetricSink(MetricSink metricSink) {
674
    metricSinks.add(checkNotNull(metricSink, "metric sink"));
1✔
675
    return this;
1✔
676
  }
677

678
  @Override
679
  public ManagedChannel build() {
680
    return new ManagedChannelOrphanWrapper(new ManagedChannelImpl(
1✔
681
        this,
682
        clientTransportFactoryBuilder.buildClientTransportFactory(),
1✔
683
        new ExponentialBackoffPolicy.Provider(),
684
        SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR),
1✔
685
        GrpcUtil.STOPWATCH_SUPPLIER,
686
        getEffectiveInterceptors(),
1✔
687
        TimeProvider.SYSTEM_TIME_PROVIDER));
688
  }
689

690
  // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know
691
  // what should be the desired behavior for retry + stats/tracing.
692
  // TODO(zdapeng): FIX IT
693
  @VisibleForTesting
694
  List<ClientInterceptor> getEffectiveInterceptors() {
695
    List<ClientInterceptor> effectiveInterceptors = new ArrayList<>(this.interceptors);
1✔
696
    boolean isGlobalInterceptorsSet = false;
1✔
697
    // TODO(dnvindhya) : Convert to Configurator
698
    List<ClientInterceptor> globalClientInterceptors =
699
        InternalGlobalInterceptors.getClientInterceptors();
1✔
700
    if (globalClientInterceptors != null) {
1✔
701
      effectiveInterceptors.addAll(globalClientInterceptors);
×
702
      isGlobalInterceptorsSet = true;
×
703
    }
704
    if (!isGlobalInterceptorsSet && statsEnabled) {
1✔
705
      ClientInterceptor statsInterceptor = null;
1✔
706

707
      if (GET_CLIENT_INTERCEPTOR_METHOD != null) {
1✔
708
        try {
709
          statsInterceptor =
1✔
710
            (ClientInterceptor) GET_CLIENT_INTERCEPTOR_METHOD
711
              .invoke(
1✔
712
                null,
713
                recordStartedRpcs,
1✔
714
                recordFinishedRpcs,
1✔
715
                recordRealTimeMetrics,
1✔
716
                recordRetryMetrics);
1✔
717
        } catch (IllegalAccessException e) {
×
718
          log.log(Level.FINE, "Unable to apply census stats", e);
×
719
        } catch (InvocationTargetException e) {
×
720
          log.log(Level.FINE, "Unable to apply census stats", e);
×
721
        }
1✔
722
      }
723

724
      if (statsInterceptor != null) {
1✔
725
        // First interceptor runs last (see ClientInterceptors.intercept()), so that no
726
        // other interceptor can override the tracer factory we set in CallOptions.
727
        effectiveInterceptors.add(0, statsInterceptor);
1✔
728
      }
729
    }
730
    if (!isGlobalInterceptorsSet && tracingEnabled) {
1✔
731
      ClientInterceptor tracingInterceptor = null;
1✔
732
      try {
733
        Class<?> censusTracingAccessor =
1✔
734
            Class.forName("io.grpc.census.InternalCensusTracingAccessor");
1✔
735
        Method getClientInterceptroMethod =
1✔
736
            censusTracingAccessor.getDeclaredMethod("getClientInterceptor");
1✔
737
        tracingInterceptor = (ClientInterceptor) getClientInterceptroMethod.invoke(null);
1✔
738
      } catch (ClassNotFoundException e) {
1✔
739
        // Replace these separate catch statements with multicatch when Android min-API >= 19
740
        log.log(Level.FINE, "Unable to apply census stats", e);
1✔
741
      } catch (NoSuchMethodException e) {
×
742
        log.log(Level.FINE, "Unable to apply census stats", e);
×
743
      } catch (IllegalAccessException e) {
×
744
        log.log(Level.FINE, "Unable to apply census stats", e);
×
745
      } catch (InvocationTargetException e) {
×
746
        log.log(Level.FINE, "Unable to apply census stats", e);
×
747
      }
1✔
748
      if (tracingInterceptor != null) {
1✔
749
        effectiveInterceptors.add(0, tracingInterceptor);
1✔
750
      }
751
    }
752
    return effectiveInterceptors;
1✔
753
  }
754

755
  /**
756
   * Returns a default port to {@link NameResolver} for use in cases where the target string doesn't
757
   * include a port. The default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}.
758
   */
759
  int getDefaultPort() {
760
    return channelBuilderDefaultPortProvider.getDefaultPort();
1✔
761
  }
762

763
  private static class DirectAddressNameResolverProvider extends NameResolverProvider {
764
    final SocketAddress address;
765
    final String authority;
766
    final Collection<Class<? extends SocketAddress>> producedSocketAddressTypes;
767

768
    DirectAddressNameResolverProvider(SocketAddress address, String authority) {
1✔
769
      this.address = address;
1✔
770
      this.authority = authority;
1✔
771
      this.producedSocketAddressTypes
1✔
772
          = Collections.singleton(address.getClass());
1✔
773
    }
1✔
774

775
    @Override
776
    public NameResolver newNameResolver(URI notUsedUri, NameResolver.Args args) {
777
      return new NameResolver() {
1✔
778
        @Override
779
        public String getServiceAuthority() {
780
          return authority;
1✔
781
        }
782

783
        @Override
784
        public void start(Listener2 listener) {
785
          listener.onResult(
1✔
786
              ResolutionResult.newBuilder()
1✔
787
                  .setAddresses(Collections.singletonList(new EquivalentAddressGroup(address)))
1✔
788
                  .setAttributes(Attributes.EMPTY)
1✔
789
                  .build());
1✔
790
        }
1✔
791

792
        @Override
793
        public void shutdown() {}
1✔
794
      };
795
    }
796

797
    @Override
798
    public String getDefaultScheme() {
799
      return DIRECT_ADDRESS_SCHEME;
1✔
800
    }
801

802
    @Override
803
    protected boolean isAvailable() {
804
      return true;
1✔
805
    }
806

807
    @Override
808
    protected int priority() {
809
      return 5;
1✔
810
    }
811

812
    @Override
813
    public Collection<Class<? extends SocketAddress>> getProducedSocketAddressTypes() {
814
      return producedSocketAddressTypes;
1✔
815
    }
816
  }
817

818
  /**
819
   * Returns the internal offload executor pool for offloading tasks.
820
   */
821
  public ObjectPool<? extends Executor> getOffloadExecutorPool() {
822
    return this.offloadExecutorPool;
1✔
823
  }
824
}
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