blob: 386d95c6bbddb45a698053d7bc578bd332d2a6fb [file] [log] [blame]
lryan56e307f2014-12-05 13:25:08 -08001/*
Carl Mastrangelo3bfd6302017-05-31 13:29:01 -07002 * Copyright 2014, gRPC Authors All rights reserved.
lryan56e307f2014-12-05 13:25:08 -08003 *
Carl Mastrangelo3bfd6302017-05-31 13:29:01 -07004 * 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
Carl Mastrangelo166108a2017-06-01 14:28:37 -07007 *
Carl Mastrangelo3bfd6302017-05-31 13:29:01 -07008 * http://www.apache.org/licenses/LICENSE-2.0
Carl Mastrangelo166108a2017-06-01 14:28:37 -07009 *
Carl Mastrangelo3bfd6302017-05-31 13:29:01 -070010 * 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.
lryan56e307f2014-12-05 13:25:08 -080015 */
16
nmittlerb687bdc2015-08-31 16:13:39 -070017package io.grpc.internal;
zhangkun8d6d12e2014-10-15 13:04:19 -070018
Kun Zhang527fd672016-06-17 09:47:41 -070019import static com.google.common.base.Preconditions.checkArgument;
Carl Mastrangelo82a79d82015-12-07 14:40:11 -080020
Kun Zhang631a9d52016-06-02 16:47:36 -070021import com.google.common.annotations.VisibleForTesting;
Kun Zhang942f4c92015-09-04 17:21:44 -070022import com.google.common.base.Preconditions;
buchgr602473d2015-11-11 12:53:08 +010023import com.google.common.util.concurrent.MoreExecutors;
Kun Zhangcce8eac2017-01-05 10:48:13 -080024import com.google.instrumentation.stats.Stats;
25import com.google.instrumentation.stats.StatsContextFactory;
Kun Zhang49bde542017-04-25 13:53:29 -070026import com.google.instrumentation.trace.Tracing;
Kun Zhang942f4c92015-09-04 17:21:44 -070027import io.grpc.Attributes;
28import io.grpc.ClientInterceptor;
ZHANG Dapeng7d048af2017-05-02 16:21:43 -070029import io.grpc.ClientStreamTracer;
Carl Mastrangelo82a79d82015-12-07 14:40:11 -080030import io.grpc.CompressorRegistry;
31import io.grpc.DecompressorRegistry;
Kun Zhang418d52d2017-03-22 18:29:31 -070032import io.grpc.EquivalentAddressGroup;
Kun Zhanga9bd9472017-02-21 17:11:03 -080033import io.grpc.LoadBalancer;
Kun Zhangd17a7b52017-01-10 15:30:12 -080034import io.grpc.ManagedChannel;
Kun Zhang942f4c92015-09-04 17:21:44 -070035import io.grpc.ManagedChannelBuilder;
36import io.grpc.NameResolver;
Eric Andersonfaa0ad72017-06-07 12:18:27 -070037import io.grpc.NameResolverProvider;
Kun Zhanga9bd9472017-02-21 17:11:03 -080038import io.grpc.PickFirstBalancerFactory;
Kun Zhang942f4c92015-09-04 17:21:44 -070039import java.net.SocketAddress;
40import java.net.URI;
Kun Zhang631a9d52016-06-02 16:47:36 -070041import java.net.URISyntaxException;
Eric Anderson0df3d5e2015-06-25 18:00:00 -070042import java.util.ArrayList;
43import java.util.Arrays;
Kun Zhang942f4c92015-09-04 17:21:44 -070044import java.util.Collections;
Eric Anderson0df3d5e2015-06-25 18:00:00 -070045import java.util.List;
Louis Ryan6a782a02015-09-03 14:27:48 -070046import java.util.concurrent.Executor;
Kun Zhang527fd672016-06-17 09:47:41 -070047import java.util.concurrent.TimeUnit;
zhangkun8d6d12e2014-10-15 13:04:19 -070048import javax.annotation.Nullable;
49
50/**
51 * The base class for channel builders.
nathanmittler0304b3d2014-10-24 13:39:13 -070052 *
nmittlerb687bdc2015-08-31 16:13:39 -070053 * @param <T> The concrete type of this builder.
zhangkun8d6d12e2014-10-15 13:04:19 -070054 */
nmittlerb687bdc2015-08-31 16:13:39 -070055public abstract class AbstractManagedChannelImplBuilder
56 <T extends AbstractManagedChannelImplBuilder<T>> extends ManagedChannelBuilder<T> {
Kun Zhang16247152015-12-08 17:52:05 -080057 private static final String DIRECT_ADDRESS_SCHEME = "directaddress";
Eric Andersonaeeebb72014-12-19 16:41:03 -080058
Kun Zhang527fd672016-06-17 09:47:41 -070059 /**
60 * An idle timeout larger than this would disable idle mode.
61 */
62 @VisibleForTesting
63 static final long IDLE_MODE_MAX_TIMEOUT_DAYS = 30;
64
65 /**
Kun Zhangd74091f2016-09-16 22:34:27 -070066 * The default idle timeout.
67 */
68 @VisibleForTesting
69 static final long IDLE_MODE_DEFAULT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(30);
70
71 /**
Kun Zhang527fd672016-06-17 09:47:41 -070072 * An idle timeout smaller than this would be capped to it.
73 */
74 @VisibleForTesting
75 static final long IDLE_MODE_MIN_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(1);
76
Eric Anderson0a8d7612017-05-23 11:08:34 -070077 private static final ObjectPool<? extends Executor> DEFAULT_EXECUTOR_POOL =
78 SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR);
79
80 private static final NameResolver.Factory DEFAULT_NAME_RESOLVER_FACTORY =
81 NameResolverProvider.asFactory();
82
83 private static final LoadBalancer.Factory DEFAULT_LOAD_BALANCER_FACTORY =
84 PickFirstBalancerFactory.getInstance();
85
86 private static final DecompressorRegistry DEFAULT_DECOMPRESSOR_REGISTRY =
87 DecompressorRegistry.getDefaultInstance();
88
89 private static final CompressorRegistry DEFAULT_COMPRESSOR_REGISTRY =
90 CompressorRegistry.getDefaultInstance();
91
92 ObjectPool<? extends Executor> executorPool = DEFAULT_EXECUTOR_POOL;
Kun Zhangd17a7b52017-01-10 15:30:12 -080093
Eric Anderson0df3d5e2015-06-25 18:00:00 -070094 private final List<ClientInterceptor> interceptors = new ArrayList<ClientInterceptor>();
zhangkun8d6d12e2014-10-15 13:04:19 -070095
Eric Anderson0a8d7612017-05-23 11:08:34 -070096 final String target;
Kun Zhang942f4c92015-09-04 17:21:44 -070097
98 @Nullable
99 private final SocketAddress directServerAddress;
100
nmittler8c1d38a2015-06-01 08:31:00 -0700101 @Nullable
Eric Anderson0a8d7612017-05-23 11:08:34 -0700102 String userAgent;
nmittler8c1d38a2015-06-01 08:31:00 -0700103
Kun Zhang942f4c92015-09-04 17:21:44 -0700104 @Nullable
Eric Anderson0a8d7612017-05-23 11:08:34 -0700105 String authorityOverride;
Kun Zhang942f4c92015-09-04 17:21:44 -0700106
Eric Anderson0a8d7612017-05-23 11:08:34 -0700107 NameResolver.Factory nameResolverFactory = DEFAULT_NAME_RESOLVER_FACTORY;
Kun Zhang942f4c92015-09-04 17:21:44 -0700108
Eric Anderson0a8d7612017-05-23 11:08:34 -0700109 LoadBalancer.Factory loadBalancerFactory = DEFAULT_LOAD_BALANCER_FACTORY;
Kun Zhang942f4c92015-09-04 17:21:44 -0700110
Eric Anderson0a8d7612017-05-23 11:08:34 -0700111 DecompressorRegistry decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
Carl Mastrangelo82a79d82015-12-07 14:40:11 -0800112
Eric Anderson0a8d7612017-05-23 11:08:34 -0700113 CompressorRegistry compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
Carl Mastrangelo82a79d82015-12-07 14:40:11 -0800114
Eric Anderson0a8d7612017-05-23 11:08:34 -0700115 long idleTimeoutMillis = IDLE_MODE_DEFAULT_TIMEOUT_MILLIS;
Kun Zhang527fd672016-06-17 09:47:41 -0700116
Carl Mastrangelofdd062c2016-10-26 16:55:41 -0700117 private int maxInboundMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE;
118
Kun Zhangbe74e972017-04-26 10:50:55 -0700119 private boolean enableStatsTagPropagation;
120 private boolean enableTracing;
121
zpencer347eb092017-05-17 17:12:45 -0700122 /**
123 * Sets the maximum message size allowed for a single gRPC frame. If an inbound messages
124 * larger than this limit is received it will not be processed and the RPC will fail with
125 * RESOURCE_EXHAUSTED.
126 */
Carl Mastrangelofdd062c2016-10-26 16:55:41 -0700127 // Can be overriden by subclasses.
128 @Override
129 public T maxInboundMessageSize(int max) {
130 checkArgument(max >= 0, "negative max");
131 maxInboundMessageSize = max;
132 return thisT();
133 }
134
135 protected final int maxInboundMessageSize() {
136 return maxInboundMessageSize;
137 }
138
Kun Zhang132f7a92016-10-06 17:15:24 -0700139 @Nullable
Kun Zhangcce8eac2017-01-05 10:48:13 -0800140 private StatsContextFactory statsFactory;
Kun Zhang132f7a92016-10-06 17:15:24 -0700141
Kun Zhangefac6792015-10-22 14:59:44 -0700142 protected AbstractManagedChannelImplBuilder(String target) {
Carl Mastrangelo12854772016-08-12 14:52:00 -0700143 this.target = Preconditions.checkNotNull(target, "target");
Kun Zhang942f4c92015-09-04 17:21:44 -0700144 this.directServerAddress = null;
145 }
146
Kun Zhang631a9d52016-06-02 16:47:36 -0700147 /**
148 * Returns a target string for the SocketAddress. It is only used as a placeholder, because
149 * DirectAddressNameResolverFactory will not actually try to use it. However, it must be a valid
150 * URI.
151 */
152 @VisibleForTesting
153 static String makeTargetStringForDirectAddress(SocketAddress address) {
154 try {
155 return new URI(DIRECT_ADDRESS_SCHEME, "", "/" + address, null).toString();
156 } catch (URISyntaxException e) {
157 // It should not happen.
158 throw new RuntimeException(e);
159 }
160 }
161
Kun Zhang942f4c92015-09-04 17:21:44 -0700162 protected AbstractManagedChannelImplBuilder(SocketAddress directServerAddress, String authority) {
Kun Zhang631a9d52016-06-02 16:47:36 -0700163 this.target = makeTargetStringForDirectAddress(directServerAddress);
Kun Zhang942f4c92015-09-04 17:21:44 -0700164 this.directServerAddress = directServerAddress;
165 this.nameResolverFactory = new DirectAddressNameResolverFactory(directServerAddress, authority);
166 }
167
Eric Anderson6122daf2015-09-03 12:14:30 -0700168 @Override
buchgr602473d2015-11-11 12:53:08 +0100169 public final T directExecutor() {
170 return executor(MoreExecutors.directExecutor());
171 }
172
173 @Override
Louis Ryan6a782a02015-09-03 14:27:48 -0700174 public final T executor(Executor executor) {
Eric Anderson0a8d7612017-05-23 11:08:34 -0700175 if (executor != null) {
176 this.executorPool = new FixedObjectPool<Executor>(executor);
177 } else {
178 this.executorPool = DEFAULT_EXECUTOR_POOL;
179 }
Eric Anderson0df3d5e2015-06-25 18:00:00 -0700180 return thisT();
181 }
182
Eric Anderson6122daf2015-09-03 12:14:30 -0700183 @Override
nmittlerb687bdc2015-08-31 16:13:39 -0700184 public final T intercept(List<ClientInterceptor> interceptors) {
Eric Anderson0df3d5e2015-06-25 18:00:00 -0700185 this.interceptors.addAll(interceptors);
186 return thisT();
187 }
188
Eric Anderson6122daf2015-09-03 12:14:30 -0700189 @Override
nmittlerb687bdc2015-08-31 16:13:39 -0700190 public final T intercept(ClientInterceptor... interceptors) {
Eric Anderson0df3d5e2015-06-25 18:00:00 -0700191 return intercept(Arrays.asList(interceptors));
192 }
193
Kun Zhang942f4c92015-09-04 17:21:44 -0700194 @Override
195 public final T nameResolverFactory(NameResolver.Factory resolverFactory) {
196 Preconditions.checkState(directServerAddress == null,
197 "directServerAddress is set (%s), which forbids the use of NameResolverFactory",
198 directServerAddress);
Eric Anderson0a8d7612017-05-23 11:08:34 -0700199 if (resolverFactory != null) {
200 this.nameResolverFactory = resolverFactory;
201 } else {
202 this.nameResolverFactory = DEFAULT_NAME_RESOLVER_FACTORY;
203 }
Kun Zhang942f4c92015-09-04 17:21:44 -0700204 return thisT();
205 }
206
207 @Override
Kun Zhanga9bd9472017-02-21 17:11:03 -0800208 public final T loadBalancerFactory(LoadBalancer.Factory loadBalancerFactory) {
Kun Zhang942f4c92015-09-04 17:21:44 -0700209 Preconditions.checkState(directServerAddress == null,
Kun Zhanga9bd9472017-02-21 17:11:03 -0800210 "directServerAddress is set (%s), which forbids the use of LoadBalancer.Factory",
Kun Zhang942f4c92015-09-04 17:21:44 -0700211 directServerAddress);
Eric Anderson0a8d7612017-05-23 11:08:34 -0700212 if (loadBalancerFactory != null) {
213 this.loadBalancerFactory = loadBalancerFactory;
214 } else {
215 this.loadBalancerFactory = DEFAULT_LOAD_BALANCER_FACTORY;
216 }
Kun Zhangd17a7b52017-01-10 15:30:12 -0800217 return thisT();
218 }
219
Carl Mastrangelo82a79d82015-12-07 14:40:11 -0800220 @Override
Carl Mastrangelo82a79d82015-12-07 14:40:11 -0800221 public final T decompressorRegistry(DecompressorRegistry registry) {
Eric Anderson0a8d7612017-05-23 11:08:34 -0700222 if (registry != null) {
223 this.decompressorRegistry = registry;
224 } else {
225 this.decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY;
226 }
Carl Mastrangelo82a79d82015-12-07 14:40:11 -0800227 return thisT();
228 }
229
230 @Override
Carl Mastrangelo82a79d82015-12-07 14:40:11 -0800231 public final T compressorRegistry(CompressorRegistry registry) {
Eric Anderson0a8d7612017-05-23 11:08:34 -0700232 if (registry != null) {
233 this.compressorRegistry = registry;
234 } else {
235 this.compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY;
236 }
Carl Mastrangelo82a79d82015-12-07 14:40:11 -0800237 return thisT();
238 }
239
Eric Anderson6122daf2015-09-03 12:14:30 -0700240 @Override
Carl Mastrangelo1cc76d82016-05-24 16:29:26 -0700241 public final T userAgent(@Nullable String userAgent) {
nmittler8c1d38a2015-06-01 08:31:00 -0700242 this.userAgent = userAgent;
nmittlerb687bdc2015-08-31 16:13:39 -0700243 return thisT();
nmittler8c1d38a2015-06-01 08:31:00 -0700244 }
245
Eric Anderson6122daf2015-09-03 12:14:30 -0700246 @Override
Kun Zhang942f4c92015-09-04 17:21:44 -0700247 public final T overrideAuthority(String authority) {
248 this.authorityOverride = checkAuthority(authority);
249 return thisT();
250 }
251
Kun Zhang527fd672016-06-17 09:47:41 -0700252 @Override
253 public final T idleTimeout(long value, TimeUnit unit) {
254 checkArgument(value > 0, "idle timeout is %s, but must be positive", value);
255 // We convert to the largest unit to avoid overflow
256 if (unit.toDays(value) >= IDLE_MODE_MAX_TIMEOUT_DAYS) {
257 // This disables idle mode
Kun Zhanga9bd9472017-02-21 17:11:03 -0800258 this.idleTimeoutMillis = ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE;
Kun Zhang527fd672016-06-17 09:47:41 -0700259 } else {
260 this.idleTimeoutMillis = Math.max(unit.toMillis(value), IDLE_MODE_MIN_TIMEOUT_MILLIS);
261 }
262 return thisT();
263 }
264
Kun Zhang132f7a92016-10-06 17:15:24 -0700265 /**
Kun Zhang737cd162017-01-20 17:20:44 -0800266 * Override the default stats implementation.
Kun Zhang132f7a92016-10-06 17:15:24 -0700267 */
268 @VisibleForTesting
Kun Zhang903197b2017-04-07 11:03:24 -0700269 protected final T statsContextFactory(StatsContextFactory statsFactory) {
Kun Zhangcce8eac2017-01-05 10:48:13 -0800270 this.statsFactory = statsFactory;
Kun Zhang132f7a92016-10-06 17:15:24 -0700271 return thisT();
272 }
273
Kun Zhang903197b2017-04-07 11:03:24 -0700274 /**
275 * Indicates whether this transport will record stats with {@link ClientStreamTracer}.
276 *
277 * <p>By default it returns {@code true}. If the transport doesn't record stats, it may override
278 * this method to return {@code false} so that the builder won't install the Census interceptor.
279 *
280 * <p>If it returns true when it shouldn't be, Census will receive incomplete stats.
281 */
282 protected boolean recordsStats() {
283 return true;
284 }
285
Kun Zhang527fd672016-06-17 09:47:41 -0700286 @VisibleForTesting
287 final long getIdleTimeoutMillis() {
288 return idleTimeoutMillis;
289 }
290
Kun Zhang942f4c92015-09-04 17:21:44 -0700291 /**
292 * Verifies the authority is valid. This method exists as an escape hatch for putting in an
293 * authority that is valid, but would fail the default validation provided by this
294 * implementation.
295 */
296 protected String checkAuthority(String authority) {
297 return GrpcUtil.checkAuthority(authority);
298 }
299
Kun Zhangbe74e972017-04-26 10:50:55 -0700300 /**
301 * Set it to true to propagate the stats tags on the wire. This will be deleted assuming always
302 * enabled once the instrumentation-java wire format is stabilized.
303 */
304 @Deprecated
305 public void setEnableStatsTagPropagation(boolean enabled) {
306 this.enableStatsTagPropagation = enabled;
307 }
308
309 /**
310 * Set it to true to record traces and propagate tracing information on the wire. This will be
311 * deleted assuming always enabled once the instrumentation-java wire format is stabilized.
312 */
313 @Deprecated
314 public void setEnableTracing(boolean enabled) {
315 this.enableTracing = enabled;
316 }
317
Kun Zhang942f4c92015-09-04 17:21:44 -0700318 @Override
Kun Zhangd17a7b52017-01-10 15:30:12 -0800319 public ManagedChannel build() {
Eric Anderson0a8d7612017-05-23 11:08:34 -0700320 return new ManagedChannelImpl(
321 this,
322 buildTransportFactory(),
323 // TODO(carl-mastrangelo): Allow clients to pass this in
324 new ExponentialBackoffPolicy.Provider(),
325 SharedResourcePool.forResource(GrpcUtil.TIMER_SERVICE),
326 SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR),
327 GrpcUtil.STOPWATCH_SUPPLIER,
328 getEffectiveInterceptors());
329 }
Kun Zhang903197b2017-04-07 11:03:24 -0700330
Eric Anderson0a8d7612017-05-23 11:08:34 -0700331 private List<ClientInterceptor> getEffectiveInterceptors() {
Kun Zhang49bde542017-04-25 13:53:29 -0700332 List<ClientInterceptor> effectiveInterceptors =
333 new ArrayList<ClientInterceptor>(this.interceptors);
Kun Zhangbe74e972017-04-26 10:50:55 -0700334 if (recordsStats()) {
Kun Zhang903197b2017-04-07 11:03:24 -0700335 StatsContextFactory statsCtxFactory =
336 this.statsFactory != null ? this.statsFactory : Stats.getStatsContextFactory();
337 if (statsCtxFactory != null) {
Kun Zhang49bde542017-04-25 13:53:29 -0700338 CensusStatsModule censusStats =
Kun Zhangbe74e972017-04-26 10:50:55 -0700339 new CensusStatsModule(
340 statsCtxFactory, GrpcUtil.STOPWATCH_SUPPLIER, enableStatsTagPropagation);
Kun Zhang903197b2017-04-07 11:03:24 -0700341 // First interceptor runs last (see ClientInterceptors.intercept()), so that no
342 // other interceptor can override the tracer factory we set in CallOptions.
Kun Zhang49bde542017-04-25 13:53:29 -0700343 effectiveInterceptors.add(0, censusStats.getClientInterceptor());
Kun Zhang903197b2017-04-07 11:03:24 -0700344 }
345 }
Kun Zhangbe74e972017-04-26 10:50:55 -0700346 if (enableTracing) {
Kun Zhang49bde542017-04-25 13:53:29 -0700347 CensusTracingModule censusTracing =
348 new CensusTracingModule(Tracing.getTracer(), Tracing.getBinaryPropagationHandler());
349 effectiveInterceptors.add(0, censusTracing.getClientInterceptor());
350 }
Eric Anderson0a8d7612017-05-23 11:08:34 -0700351 return effectiveInterceptors;
zhangkun8d6d12e2014-10-15 13:04:19 -0700352 }
353
354 /**
Kun Zhangedd57c92015-10-27 12:47:29 -0700355 * Subclasses should override this method to provide the {@link ClientTransportFactory}
356 * appropriate for this channel. This method is meant for Transport implementors and should not
357 * be used by normal users.
zhangkun8d6d12e2014-10-15 13:04:19 -0700358 */
nmittler777e9282015-08-19 10:01:52 -0700359 protected abstract ClientTransportFactory buildTransportFactory();
Kun Zhang942f4c92015-09-04 17:21:44 -0700360
Kun Zhangedd57c92015-10-27 12:47:29 -0700361 /**
362 * Subclasses can override this method to provide additional parameters to {@link
363 * NameResolver.Factory#newNameResolver}. The default implementation returns {@link
Carl Mastrangelo82a79d82015-12-07 14:40:11 -0800364 * Attributes#EMPTY}.
Kun Zhangedd57c92015-10-27 12:47:29 -0700365 */
366 protected Attributes getNameResolverParams() {
367 return Attributes.EMPTY;
368 }
369
Kun Zhang942f4c92015-09-04 17:21:44 -0700370 private static class DirectAddressNameResolverFactory extends NameResolver.Factory {
371 final SocketAddress address;
372 final String authority;
373
374 DirectAddressNameResolverFactory(SocketAddress address, String authority) {
375 this.address = address;
376 this.authority = authority;
377 }
378
379 @Override
Kun Zhangedd57c92015-10-27 12:47:29 -0700380 public NameResolver newNameResolver(URI notUsedUri, Attributes params) {
Kun Zhang942f4c92015-09-04 17:21:44 -0700381 return new NameResolver() {
382 @Override
383 public String getServiceAuthority() {
384 return authority;
385 }
386
387 @Override
388 public void start(final Listener listener) {
Kun Zhang418d52d2017-03-22 18:29:31 -0700389 listener.onAddresses(
390 Collections.singletonList(new EquivalentAddressGroup(address)),
Kun Zhang942f4c92015-09-04 17:21:44 -0700391 Attributes.EMPTY);
392 }
393
394 @Override
395 public void shutdown() {}
396 };
397 }
Kun Zhang16247152015-12-08 17:52:05 -0800398
399 @Override
400 public String getDefaultScheme() {
401 return DIRECT_ADDRESS_SCHEME;
402 }
Kun Zhang942f4c92015-09-04 17:21:44 -0700403 }
Carl Mastrangelo84934982017-01-11 12:46:47 -0800404
405 /**
406 * Returns the correctly typed version of the builder.
407 */
408 private T thisT() {
409 @SuppressWarnings("unchecked")
410 T thisT = (T) this;
411 return thisT;
412 }
zhangkun8d6d12e2014-10-15 13:04:19 -0700413}