• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 import com.google.common.annotations.VisibleForTesting;
22 import com.google.common.base.Preconditions;
23 import com.google.common.util.concurrent.MoreExecutors;
24 import com.google.errorprone.annotations.DoNotCall;
25 import io.grpc.Attributes;
26 import io.grpc.BinaryLog;
27 import io.grpc.CallCredentials;
28 import io.grpc.ChannelCredentials;
29 import io.grpc.ClientInterceptor;
30 import io.grpc.CompressorRegistry;
31 import io.grpc.DecompressorRegistry;
32 import io.grpc.EquivalentAddressGroup;
33 import io.grpc.InternalChannelz;
34 import io.grpc.InternalGlobalInterceptors;
35 import io.grpc.ManagedChannel;
36 import io.grpc.ManagedChannelBuilder;
37 import io.grpc.NameResolver;
38 import io.grpc.NameResolverRegistry;
39 import io.grpc.ProxyDetector;
40 import java.lang.reflect.InvocationTargetException;
41 import java.lang.reflect.Method;
42 import java.net.SocketAddress;
43 import java.net.URI;
44 import java.net.URISyntaxException;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.LinkedHashMap;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.concurrent.Executor;
52 import java.util.concurrent.TimeUnit;
53 import java.util.logging.Level;
54 import java.util.logging.Logger;
55 import javax.annotation.Nullable;
56 
57 /**
58  * Default managed channel builder, for usage in Transport implementations.
59  */
60 public final class ManagedChannelImplBuilder
61     extends ManagedChannelBuilder<ManagedChannelImplBuilder> {
62   private static final String DIRECT_ADDRESS_SCHEME = "directaddress";
63 
64   private static final Logger log = Logger.getLogger(ManagedChannelImplBuilder.class.getName());
65 
66   @DoNotCall("ClientTransportFactoryBuilder is required, use a constructor")
forAddress(String name, int port)67   public static ManagedChannelBuilder<?> forAddress(String name, int port) {
68     throw new UnsupportedOperationException(
69         "ClientTransportFactoryBuilder is required, use a constructor");
70   }
71 
72   @DoNotCall("ClientTransportFactoryBuilder is required, use a constructor")
forTarget(String target)73   public static ManagedChannelBuilder<?> forTarget(String target) {
74     throw new UnsupportedOperationException(
75         "ClientTransportFactoryBuilder is required, use a constructor");
76   }
77 
78   /**
79    * An idle timeout larger than this would disable idle mode.
80    */
81   @VisibleForTesting
82   static final long IDLE_MODE_MAX_TIMEOUT_DAYS = 30;
83 
84   /**
85    * The default idle timeout.
86    */
87   @VisibleForTesting
88   static final long IDLE_MODE_DEFAULT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(30);
89 
90   /**
91    * An idle timeout smaller than this would be capped to it.
92    */
93   static final long IDLE_MODE_MIN_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(1);
94 
95   private static final ObjectPool<? extends Executor> DEFAULT_EXECUTOR_POOL =
96       SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR);
97 
98   private static final DecompressorRegistry DEFAULT_DECOMPRESSOR_REGISTRY =
99       DecompressorRegistry.getDefaultInstance();
100 
101   private static final CompressorRegistry DEFAULT_COMPRESSOR_REGISTRY =
102       CompressorRegistry.getDefaultInstance();
103 
104   private static final long DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES = 1L << 24;  // 16M
105   private static final long DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES = 1L << 20; // 1M
106 
107   ObjectPool<? extends Executor> executorPool = DEFAULT_EXECUTOR_POOL;
108 
109   ObjectPool<? extends Executor> offloadExecutorPool = DEFAULT_EXECUTOR_POOL;
110 
111   private final List<ClientInterceptor> interceptors = new ArrayList<>();
112   final NameResolverRegistry nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
113 
114   // Access via getter, which may perform authority override as needed
115   NameResolver.Factory nameResolverFactory = nameResolverRegistry.asFactory();
116 
117   final String target;
118   @Nullable
119   final ChannelCredentials channelCredentials;
120   @Nullable
121   final CallCredentials callCredentials;
122 
123   @Nullable
124   private final SocketAddress directServerAddress;
125 
126   @Nullable
127   String userAgent;
128 
129   @Nullable
130   String authorityOverride;
131 
132   String defaultLbPolicy = GrpcUtil.DEFAULT_LB_POLICY;
133 
134   boolean fullStreamDecompression;
135 
136   DecompressorRegistry decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
137 
138   CompressorRegistry compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
139 
140   long idleTimeoutMillis = IDLE_MODE_DEFAULT_TIMEOUT_MILLIS;
141 
142   int maxRetryAttempts = 5;
143   int maxHedgedAttempts = 5;
144   long retryBufferSize = DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES;
145   long perRpcBufferLimit = DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES;
146   boolean retryEnabled = true;
147 
148   InternalChannelz channelz = InternalChannelz.instance();
149   int maxTraceEvents;
150 
151   @Nullable
152   Map<String, ?> defaultServiceConfig;
153   boolean lookUpServiceConfig = true;
154 
155   @Nullable
156   BinaryLog binlog;
157 
158   @Nullable
159   ProxyDetector proxyDetector;
160 
161   private boolean authorityCheckerDisabled;
162   private boolean statsEnabled = true;
163   private boolean recordStartedRpcs = true;
164   private boolean recordFinishedRpcs = true;
165   private boolean recordRealTimeMetrics = false;
166   private boolean recordRetryMetrics = true;
167   private boolean tracingEnabled = true;
168 
169   /**
170    * An interface for Transport implementors to provide the {@link ClientTransportFactory}
171    * appropriate for the channel.
172    */
173   public interface ClientTransportFactoryBuilder {
buildClientTransportFactory()174     ClientTransportFactory buildClientTransportFactory();
175   }
176 
177   /**
178    * Convenience ClientTransportFactoryBuilder, throws UnsupportedOperationException().
179    */
180   public static class UnsupportedClientTransportFactoryBuilder implements
181       ClientTransportFactoryBuilder {
182     @Override
buildClientTransportFactory()183     public ClientTransportFactory buildClientTransportFactory() {
184       throw new UnsupportedOperationException();
185     }
186   }
187 
188   /**
189    * An interface for Transport implementors to provide a default port to {@link
190    * io.grpc.NameResolver} for use in cases where the target string doesn't include a port. The
191    * default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}.
192    */
193   public interface ChannelBuilderDefaultPortProvider {
getDefaultPort()194     int getDefaultPort();
195   }
196 
197   /**
198    * Default implementation of {@link ChannelBuilderDefaultPortProvider} that returns a fixed port.
199    */
200   public static final class FixedPortProvider implements ChannelBuilderDefaultPortProvider {
201     private final int port;
202 
FixedPortProvider(int port)203     public FixedPortProvider(int port) {
204       this.port = port;
205     }
206 
207     @Override
getDefaultPort()208     public int getDefaultPort() {
209       return port;
210     }
211   }
212 
213   private static final class ManagedChannelDefaultPortProvider implements
214       ChannelBuilderDefaultPortProvider {
215     @Override
getDefaultPort()216     public int getDefaultPort() {
217       return GrpcUtil.DEFAULT_PORT_SSL;
218     }
219   }
220 
221   private final ClientTransportFactoryBuilder clientTransportFactoryBuilder;
222   private final ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider;
223 
224   /**
225    * Creates a new managed channel builder with a target string, which can be either a valid {@link
226    * io.grpc.NameResolver}-compliant URI, or an authority string. Transport implementors must
227    * provide client transport factory builder, and may set custom channel default port provider.
228    */
ManagedChannelImplBuilder(String target, ClientTransportFactoryBuilder clientTransportFactoryBuilder, @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider)229   public ManagedChannelImplBuilder(String target,
230       ClientTransportFactoryBuilder clientTransportFactoryBuilder,
231       @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
232     this(target, null, null, clientTransportFactoryBuilder, channelBuilderDefaultPortProvider);
233   }
234 
235   /**
236    * Creates a new managed channel builder with a target string, which can be either a valid {@link
237    * io.grpc.NameResolver}-compliant URI, or an authority string. Transport implementors must
238    * provide client transport factory builder, and may set custom channel default port provider.
239    *
240    * @param channelCreds The ChannelCredentials provided by the user. These may be used when
241    *     creating derivative channels.
242    */
ManagedChannelImplBuilder( String target, @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds, ClientTransportFactoryBuilder clientTransportFactoryBuilder, @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider)243   public ManagedChannelImplBuilder(
244       String target, @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds,
245       ClientTransportFactoryBuilder clientTransportFactoryBuilder,
246       @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
247     this.target = Preconditions.checkNotNull(target, "target");
248     this.channelCredentials = channelCreds;
249     this.callCredentials = callCreds;
250     this.clientTransportFactoryBuilder = Preconditions
251         .checkNotNull(clientTransportFactoryBuilder, "clientTransportFactoryBuilder");
252     this.directServerAddress = null;
253 
254     if (channelBuilderDefaultPortProvider != null) {
255       this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider;
256     } else {
257       this.channelBuilderDefaultPortProvider = new ManagedChannelDefaultPortProvider();
258     }
259   }
260 
261   /**
262    * Returns a target string for the SocketAddress. It is only used as a placeholder, because
263    * DirectAddressNameResolverFactory will not actually try to use it. However, it must be a valid
264    * URI.
265    */
266   @VisibleForTesting
makeTargetStringForDirectAddress(SocketAddress address)267   static String makeTargetStringForDirectAddress(SocketAddress address) {
268     try {
269       return new URI(DIRECT_ADDRESS_SCHEME, "", "/" + address, null).toString();
270     } catch (URISyntaxException e) {
271       // It should not happen.
272       throw new RuntimeException(e);
273     }
274   }
275 
276   /**
277    * Creates a new managed channel builder with the given server address, authority string of the
278    * channel. Transport implementors must provide client transport factory builder, and may set
279    * custom channel default port provider.
280    */
ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority, ClientTransportFactoryBuilder clientTransportFactoryBuilder, @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider)281   public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
282       ClientTransportFactoryBuilder clientTransportFactoryBuilder,
283       @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
284     this(directServerAddress, authority, null, null, clientTransportFactoryBuilder,
285         channelBuilderDefaultPortProvider);
286   }
287 
288   /**
289    * Creates a new managed channel builder with the given server address, authority string of the
290    * channel. Transport implementors must provide client transport factory builder, and may set
291    * custom channel default port provider.
292    *
293    * @param channelCreds The ChannelCredentials provided by the user. These may be used when
294    *     creating derivative channels.
295    */
ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority, @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds, ClientTransportFactoryBuilder clientTransportFactoryBuilder, @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider)296   public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority,
297       @Nullable ChannelCredentials channelCreds, @Nullable CallCredentials callCreds,
298       ClientTransportFactoryBuilder clientTransportFactoryBuilder,
299       @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) {
300     this.target = makeTargetStringForDirectAddress(directServerAddress);
301     this.channelCredentials = channelCreds;
302     this.callCredentials = callCreds;
303     this.clientTransportFactoryBuilder = Preconditions
304         .checkNotNull(clientTransportFactoryBuilder, "clientTransportFactoryBuilder");
305     this.directServerAddress = directServerAddress;
306     this.nameResolverFactory = new DirectAddressNameResolverFactory(directServerAddress, authority);
307 
308     if (channelBuilderDefaultPortProvider != null) {
309       this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider;
310     } else {
311       this.channelBuilderDefaultPortProvider = new ManagedChannelDefaultPortProvider();
312     }
313   }
314 
315   @Override
directExecutor()316   public ManagedChannelImplBuilder directExecutor() {
317     return executor(MoreExecutors.directExecutor());
318   }
319 
320   @Override
executor(Executor executor)321   public ManagedChannelImplBuilder executor(Executor executor) {
322     if (executor != null) {
323       this.executorPool = new FixedObjectPool<>(executor);
324     } else {
325       this.executorPool = DEFAULT_EXECUTOR_POOL;
326     }
327     return this;
328   }
329 
330   @Override
offloadExecutor(Executor executor)331   public ManagedChannelImplBuilder offloadExecutor(Executor executor) {
332     if (executor != null) {
333       this.offloadExecutorPool = new FixedObjectPool<>(executor);
334     } else {
335       this.offloadExecutorPool = DEFAULT_EXECUTOR_POOL;
336     }
337     return this;
338   }
339 
340   @Override
intercept(List<ClientInterceptor> interceptors)341   public ManagedChannelImplBuilder intercept(List<ClientInterceptor> interceptors) {
342     this.interceptors.addAll(interceptors);
343     return this;
344   }
345 
346   @Override
intercept(ClientInterceptor... interceptors)347   public ManagedChannelImplBuilder intercept(ClientInterceptor... interceptors) {
348     return intercept(Arrays.asList(interceptors));
349   }
350 
351   @Deprecated
352   @Override
nameResolverFactory(NameResolver.Factory resolverFactory)353   public ManagedChannelImplBuilder nameResolverFactory(NameResolver.Factory resolverFactory) {
354     Preconditions.checkState(directServerAddress == null,
355         "directServerAddress is set (%s), which forbids the use of NameResolverFactory",
356         directServerAddress);
357     if (resolverFactory != null) {
358       this.nameResolverFactory = resolverFactory;
359     } else {
360       this.nameResolverFactory = nameResolverRegistry.asFactory();
361     }
362     return this;
363   }
364 
365   @Override
defaultLoadBalancingPolicy(String policy)366   public ManagedChannelImplBuilder defaultLoadBalancingPolicy(String policy) {
367     Preconditions.checkState(directServerAddress == null,
368         "directServerAddress is set (%s), which forbids the use of load-balancing policy",
369         directServerAddress);
370     Preconditions.checkArgument(policy != null, "policy cannot be null");
371     this.defaultLbPolicy = policy;
372     return this;
373   }
374 
375   @Override
enableFullStreamDecompression()376   public ManagedChannelImplBuilder enableFullStreamDecompression() {
377     this.fullStreamDecompression = true;
378     return this;
379   }
380 
381   @Override
decompressorRegistry(DecompressorRegistry registry)382   public ManagedChannelImplBuilder decompressorRegistry(DecompressorRegistry registry) {
383     if (registry != null) {
384       this.decompressorRegistry = registry;
385     } else {
386       this.decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
387     }
388     return this;
389   }
390 
391   @Override
compressorRegistry(CompressorRegistry registry)392   public ManagedChannelImplBuilder compressorRegistry(CompressorRegistry registry) {
393     if (registry != null) {
394       this.compressorRegistry = registry;
395     } else {
396       this.compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
397     }
398     return this;
399   }
400 
401   @Override
userAgent(@ullable String userAgent)402   public ManagedChannelImplBuilder userAgent(@Nullable String userAgent) {
403     this.userAgent = userAgent;
404     return this;
405   }
406 
407   @Override
overrideAuthority(String authority)408   public ManagedChannelImplBuilder overrideAuthority(String authority) {
409     this.authorityOverride = checkAuthority(authority);
410     return this;
411   }
412 
413   @Override
idleTimeout(long value, TimeUnit unit)414   public ManagedChannelImplBuilder idleTimeout(long value, TimeUnit unit) {
415     checkArgument(value > 0, "idle timeout is %s, but must be positive", value);
416     // We convert to the largest unit to avoid overflow
417     if (unit.toDays(value) >= IDLE_MODE_MAX_TIMEOUT_DAYS) {
418       // This disables idle mode
419       this.idleTimeoutMillis = ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE;
420     } else {
421       this.idleTimeoutMillis = Math.max(unit.toMillis(value), IDLE_MODE_MIN_TIMEOUT_MILLIS);
422     }
423     return this;
424   }
425 
426   @Override
maxRetryAttempts(int maxRetryAttempts)427   public ManagedChannelImplBuilder maxRetryAttempts(int maxRetryAttempts) {
428     this.maxRetryAttempts = maxRetryAttempts;
429     return this;
430   }
431 
432   @Override
maxHedgedAttempts(int maxHedgedAttempts)433   public ManagedChannelImplBuilder maxHedgedAttempts(int maxHedgedAttempts) {
434     this.maxHedgedAttempts = maxHedgedAttempts;
435     return this;
436   }
437 
438   @Override
retryBufferSize(long bytes)439   public ManagedChannelImplBuilder retryBufferSize(long bytes) {
440     checkArgument(bytes > 0L, "retry buffer size must be positive");
441     retryBufferSize = bytes;
442     return this;
443   }
444 
445   @Override
perRpcBufferLimit(long bytes)446   public ManagedChannelImplBuilder perRpcBufferLimit(long bytes) {
447     checkArgument(bytes > 0L, "per RPC buffer limit must be positive");
448     perRpcBufferLimit = bytes;
449     return this;
450   }
451 
452   @Override
disableRetry()453   public ManagedChannelImplBuilder disableRetry() {
454     retryEnabled = false;
455     return this;
456   }
457 
458   @Override
enableRetry()459   public ManagedChannelImplBuilder enableRetry() {
460     retryEnabled = true;
461     return this;
462   }
463 
464   @Override
setBinaryLog(BinaryLog binlog)465   public ManagedChannelImplBuilder setBinaryLog(BinaryLog binlog) {
466     this.binlog = binlog;
467     return this;
468   }
469 
470   @Override
maxTraceEvents(int maxTraceEvents)471   public ManagedChannelImplBuilder maxTraceEvents(int maxTraceEvents) {
472     checkArgument(maxTraceEvents >= 0, "maxTraceEvents must be non-negative");
473     this.maxTraceEvents = maxTraceEvents;
474     return this;
475   }
476 
477   @Override
proxyDetector(@ullable ProxyDetector proxyDetector)478   public ManagedChannelImplBuilder proxyDetector(@Nullable ProxyDetector proxyDetector) {
479     this.proxyDetector = proxyDetector;
480     return this;
481   }
482 
483   @Override
defaultServiceConfig(@ullable Map<String, ?> serviceConfig)484   public ManagedChannelImplBuilder defaultServiceConfig(@Nullable Map<String, ?> serviceConfig) {
485     // TODO(notcarl): use real parsing
486     defaultServiceConfig = checkMapEntryTypes(serviceConfig);
487     return this;
488   }
489 
490   @Nullable
checkMapEntryTypes(@ullable Map<?, ?> map)491   private static Map<String, ?> checkMapEntryTypes(@Nullable Map<?, ?> map) {
492     if (map == null) {
493       return null;
494     }
495     // Not using ImmutableMap.Builder because of extra guava dependency for Android.
496     Map<String, Object> parsedMap = new LinkedHashMap<>();
497     for (Map.Entry<?, ?> entry : map.entrySet()) {
498       checkArgument(
499           entry.getKey() instanceof String,
500           "The key of the entry '%s' is not of String type", entry);
501 
502       String key = (String) entry.getKey();
503       Object value = entry.getValue();
504       if (value == null) {
505         parsedMap.put(key, null);
506       } else if (value instanceof Map) {
507         parsedMap.put(key, checkMapEntryTypes((Map<?, ?>) value));
508       } else if (value instanceof List) {
509         parsedMap.put(key, checkListEntryTypes((List<?>) value));
510       } else if (value instanceof String) {
511         parsedMap.put(key, value);
512       } else if (value instanceof Double) {
513         parsedMap.put(key, value);
514       } else if (value instanceof Boolean) {
515         parsedMap.put(key, value);
516       } else {
517         throw new IllegalArgumentException(
518             "The value of the map entry '" + entry + "' is of type '" + value.getClass()
519                 + "', which is not supported");
520       }
521     }
522     return Collections.unmodifiableMap(parsedMap);
523   }
524 
checkListEntryTypes(List<?> list)525   private static List<?> checkListEntryTypes(List<?> list) {
526     List<Object> parsedList = new ArrayList<>(list.size());
527     for (Object value : list) {
528       if (value == null) {
529         parsedList.add(null);
530       } else if (value instanceof Map) {
531         parsedList.add(checkMapEntryTypes((Map<?, ?>) value));
532       } else if (value instanceof List) {
533         parsedList.add(checkListEntryTypes((List<?>) value));
534       } else if (value instanceof String) {
535         parsedList.add(value);
536       } else if (value instanceof Double) {
537         parsedList.add(value);
538       } else if (value instanceof Boolean) {
539         parsedList.add(value);
540       } else {
541         throw new IllegalArgumentException(
542             "The entry '" + value + "' is of type '" + value.getClass()
543                 + "', which is not supported");
544       }
545     }
546     return Collections.unmodifiableList(parsedList);
547   }
548 
549   @Override
disableServiceConfigLookUp()550   public ManagedChannelImplBuilder disableServiceConfigLookUp() {
551     this.lookUpServiceConfig = false;
552     return this;
553   }
554 
555   /**
556    * Disable or enable stats features. Enabled by default.
557    *
558    * <p>For the current release, calling {@code setStatsEnabled(true)} may have a side effect that
559    * disables retry.
560    */
setStatsEnabled(boolean value)561   public void setStatsEnabled(boolean value) {
562     statsEnabled = value;
563   }
564 
565   /**
566    * Disable or enable stats recording for RPC upstarts.  Effective only if {@link
567    * #setStatsEnabled} is set to true.  Enabled by default.
568    */
setStatsRecordStartedRpcs(boolean value)569   public void setStatsRecordStartedRpcs(boolean value) {
570     recordStartedRpcs = value;
571   }
572 
573   /**
574    * Disable or enable stats recording for RPC completions.  Effective only if {@link
575    * #setStatsEnabled} is set to true.  Enabled by default.
576    */
setStatsRecordFinishedRpcs(boolean value)577   public void setStatsRecordFinishedRpcs(boolean value) {
578     recordFinishedRpcs = value;
579   }
580 
581   /**
582    * Disable or enable real-time metrics recording.  Effective only if {@link #setStatsEnabled} is
583    * set to true.  Disabled by default.
584    */
setStatsRecordRealTimeMetrics(boolean value)585   public void setStatsRecordRealTimeMetrics(boolean value) {
586     recordRealTimeMetrics = value;
587   }
588 
setStatsRecordRetryMetrics(boolean value)589   public void setStatsRecordRetryMetrics(boolean value) {
590     recordRetryMetrics = value;
591   }
592 
593   /**
594    * Disable or enable tracing features.  Enabled by default.
595    */
setTracingEnabled(boolean value)596   public void setTracingEnabled(boolean value) {
597     tracingEnabled = value;
598   }
599 
600   /**
601    * Verifies the authority is valid.
602    */
603   @VisibleForTesting
checkAuthority(String authority)604   String checkAuthority(String authority) {
605     if (authorityCheckerDisabled) {
606       return authority;
607     }
608     return GrpcUtil.checkAuthority(authority);
609   }
610 
611   /** Disable the check whether the authority is valid. */
disableCheckAuthority()612   public ManagedChannelImplBuilder disableCheckAuthority() {
613     authorityCheckerDisabled = true;
614     return this;
615   }
616 
617   /** Enable previously disabled authority check. */
enableCheckAuthority()618   public ManagedChannelImplBuilder enableCheckAuthority() {
619     authorityCheckerDisabled = false;
620     return this;
621   }
622 
623   @Override
build()624   public ManagedChannel build() {
625     return new ManagedChannelOrphanWrapper(new ManagedChannelImpl(
626         this,
627         clientTransportFactoryBuilder.buildClientTransportFactory(),
628         new ExponentialBackoffPolicy.Provider(),
629         SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR),
630         GrpcUtil.STOPWATCH_SUPPLIER,
631         getEffectiveInterceptors(),
632         TimeProvider.SYSTEM_TIME_PROVIDER));
633   }
634 
635   // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know
636   // what should be the desired behavior for retry + stats/tracing.
637   // TODO(zdapeng): FIX IT
638   @VisibleForTesting
getEffectiveInterceptors()639   List<ClientInterceptor> getEffectiveInterceptors() {
640     List<ClientInterceptor> effectiveInterceptors = new ArrayList<>(this.interceptors);
641     boolean isGlobalInterceptorsSet = false;
642     List<ClientInterceptor> globalClientInterceptors =
643         InternalGlobalInterceptors.getClientInterceptors();
644     if (globalClientInterceptors != null) {
645       effectiveInterceptors.addAll(globalClientInterceptors);
646       isGlobalInterceptorsSet = true;
647     }
648     if (!isGlobalInterceptorsSet && statsEnabled) {
649       ClientInterceptor statsInterceptor = null;
650       try {
651         Class<?> censusStatsAccessor =
652             Class.forName("io.grpc.census.InternalCensusStatsAccessor");
653         Method getClientInterceptorMethod =
654             censusStatsAccessor.getDeclaredMethod(
655                 "getClientInterceptor",
656                 boolean.class,
657                 boolean.class,
658                 boolean.class,
659                 boolean.class);
660         statsInterceptor =
661             (ClientInterceptor) getClientInterceptorMethod
662                 .invoke(
663                     null,
664                     recordStartedRpcs,
665                     recordFinishedRpcs,
666                     recordRealTimeMetrics,
667                     recordRetryMetrics);
668       } catch (ClassNotFoundException e) {
669         // Replace these separate catch statements with multicatch when Android min-API >= 19
670         log.log(Level.FINE, "Unable to apply census stats", e);
671       } catch (NoSuchMethodException e) {
672         log.log(Level.FINE, "Unable to apply census stats", e);
673       } catch (IllegalAccessException e) {
674         log.log(Level.FINE, "Unable to apply census stats", e);
675       } catch (InvocationTargetException e) {
676         log.log(Level.FINE, "Unable to apply census stats", e);
677       }
678       if (statsInterceptor != null) {
679         // First interceptor runs last (see ClientInterceptors.intercept()), so that no
680         // other interceptor can override the tracer factory we set in CallOptions.
681         effectiveInterceptors.add(0, statsInterceptor);
682       }
683     }
684     if (!isGlobalInterceptorsSet && tracingEnabled) {
685       ClientInterceptor tracingInterceptor = null;
686       try {
687         Class<?> censusTracingAccessor =
688             Class.forName("io.grpc.census.InternalCensusTracingAccessor");
689         Method getClientInterceptroMethod =
690             censusTracingAccessor.getDeclaredMethod("getClientInterceptor");
691         tracingInterceptor = (ClientInterceptor) getClientInterceptroMethod.invoke(null);
692       } catch (ClassNotFoundException e) {
693         // Replace these separate catch statements with multicatch when Android min-API >= 19
694         log.log(Level.FINE, "Unable to apply census stats", e);
695       } catch (NoSuchMethodException e) {
696         log.log(Level.FINE, "Unable to apply census stats", e);
697       } catch (IllegalAccessException e) {
698         log.log(Level.FINE, "Unable to apply census stats", e);
699       } catch (InvocationTargetException e) {
700         log.log(Level.FINE, "Unable to apply census stats", e);
701       }
702       if (tracingInterceptor != null) {
703         effectiveInterceptors.add(0, tracingInterceptor);
704       }
705     }
706     return effectiveInterceptors;
707   }
708 
709   /**
710    * Returns a default port to {@link NameResolver} for use in cases where the target string doesn't
711    * include a port. The default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}.
712    */
getDefaultPort()713   int getDefaultPort() {
714     return channelBuilderDefaultPortProvider.getDefaultPort();
715   }
716 
717   private static class DirectAddressNameResolverFactory extends NameResolver.Factory {
718     final SocketAddress address;
719     final String authority;
720 
DirectAddressNameResolverFactory(SocketAddress address, String authority)721     DirectAddressNameResolverFactory(SocketAddress address, String authority) {
722       this.address = address;
723       this.authority = authority;
724     }
725 
726     @Override
newNameResolver(URI notUsedUri, NameResolver.Args args)727     public NameResolver newNameResolver(URI notUsedUri, NameResolver.Args args) {
728       return new NameResolver() {
729         @Override
730         public String getServiceAuthority() {
731           return authority;
732         }
733 
734         @Override
735         public void start(Listener2 listener) {
736           listener.onResult(
737               ResolutionResult.newBuilder()
738                   .setAddresses(Collections.singletonList(new EquivalentAddressGroup(address)))
739                   .setAttributes(Attributes.EMPTY)
740                   .build());
741         }
742 
743         @Override
744         public void shutdown() {}
745       };
746     }
747 
748     @Override
getDefaultScheme()749     public String getDefaultScheme() {
750       return DIRECT_ADDRESS_SCHEME;
751     }
752   }
753 
754   /**
755    * Returns the internal offload executor pool for offloading tasks.
756    */
757   public ObjectPool<? extends Executor> getOffloadExecutorPool() {
758     return this.offloadExecutorPool;
759   }
760 }
761