diff --git a/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java b/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java deleted file mode 100644 index aac6c25a8ee..00000000000 --- a/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java +++ /dev/null @@ -1,670 +0,0 @@ -/* - * Copyright 2014 The gRPC Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.grpc.internal; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.MoreExecutors; -import io.grpc.Attributes; -import io.grpc.BinaryLog; -import io.grpc.ClientInterceptor; -import io.grpc.CompressorRegistry; -import io.grpc.DecompressorRegistry; -import io.grpc.EquivalentAddressGroup; -import io.grpc.InternalChannelz; -import io.grpc.ManagedChannel; -import io.grpc.ManagedChannelBuilder; -import io.grpc.NameResolver; -import io.grpc.NameResolverRegistry; -import io.grpc.ProxyDetector; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.SocketAddress; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.Nullable; - -/** - * Abstract base class for channel builders. - * - * @param The concrete type of this builder. - */ -public abstract class AbstractManagedChannelImplBuilder - > extends ManagedChannelBuilder { - private static final String DIRECT_ADDRESS_SCHEME = "directaddress"; - - private static final Logger log = - Logger.getLogger(AbstractManagedChannelImplBuilder.class.getName()); - - public static ManagedChannelBuilder forAddress(String name, int port) { - throw new UnsupportedOperationException("Subclass failed to hide static factory"); - } - - public static ManagedChannelBuilder forTarget(String target) { - throw new UnsupportedOperationException("Subclass failed to hide static factory"); - } - - /** - * An idle timeout larger than this would disable idle mode. - */ - @VisibleForTesting - static final long IDLE_MODE_MAX_TIMEOUT_DAYS = 30; - - /** - * The default idle timeout. - */ - @VisibleForTesting - static final long IDLE_MODE_DEFAULT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(30); - - /** - * An idle timeout smaller than this would be capped to it. - */ - static final long IDLE_MODE_MIN_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(1); - - private static final ObjectPool DEFAULT_EXECUTOR_POOL = - SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR); - - private static final DecompressorRegistry DEFAULT_DECOMPRESSOR_REGISTRY = - DecompressorRegistry.getDefaultInstance(); - - private static final CompressorRegistry DEFAULT_COMPRESSOR_REGISTRY = - CompressorRegistry.getDefaultInstance(); - - private static final long DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES = 1L << 24; // 16M - private static final long DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES = 1L << 20; // 1M - - ObjectPool executorPool = DEFAULT_EXECUTOR_POOL; - - ObjectPool offloadExecutorPool = DEFAULT_EXECUTOR_POOL; - - private final List interceptors = new ArrayList<>(); - final NameResolverRegistry nameResolverRegistry = NameResolverRegistry.getDefaultRegistry(); - - // Access via getter, which may perform authority override as needed - private NameResolver.Factory nameResolverFactory = nameResolverRegistry.asFactory(); - - final String target; - - @Nullable - private final SocketAddress directServerAddress; - - @Nullable - String userAgent; - - @VisibleForTesting - @Nullable - String authorityOverride; - - String defaultLbPolicy = GrpcUtil.DEFAULT_LB_POLICY; - - boolean fullStreamDecompression; - - DecompressorRegistry decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY; - - CompressorRegistry compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY; - - long idleTimeoutMillis = IDLE_MODE_DEFAULT_TIMEOUT_MILLIS; - - int maxRetryAttempts = 5; - int maxHedgedAttempts = 5; - long retryBufferSize = DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES; - long perRpcBufferLimit = DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES; - boolean retryEnabled = false; // TODO(zdapeng): default to true - // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know - // what should be the desired behavior for retry + stats/tracing. - // TODO(zdapeng): delete me - boolean temporarilyDisableRetry; - - InternalChannelz channelz = InternalChannelz.instance(); - int maxTraceEvents; - - @Nullable - Map defaultServiceConfig; - boolean lookUpServiceConfig = true; - - protected TransportTracer.Factory transportTracerFactory = TransportTracer.getDefaultFactory(); - - private int maxInboundMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE; - - @Nullable - BinaryLog binlog; - - @Nullable - ProxyDetector proxyDetector; - - /** - * Sets the maximum message size allowed for a single gRPC frame. If an inbound messages - * larger than this limit is received it will not be processed and the RPC will fail with - * RESOURCE_EXHAUSTED. - */ - // Can be overridden by subclasses. - @Override - public T maxInboundMessageSize(int max) { - checkArgument(max >= 0, "negative max"); - maxInboundMessageSize = max; - return thisT(); - } - - protected final int maxInboundMessageSize() { - return maxInboundMessageSize; - } - - private boolean statsEnabled = true; - private boolean recordStartedRpcs = true; - private boolean recordFinishedRpcs = true; - private boolean recordRealTimeMetrics = false; - private boolean tracingEnabled = true; - - protected AbstractManagedChannelImplBuilder(String target) { - this.target = Preconditions.checkNotNull(target, "target"); - this.directServerAddress = null; - } - - /** - * Returns a target string for the SocketAddress. It is only used as a placeholder, because - * DirectAddressNameResolverFactory will not actually try to use it. However, it must be a valid - * URI. - */ - @VisibleForTesting - static String makeTargetStringForDirectAddress(SocketAddress address) { - try { - return new URI(DIRECT_ADDRESS_SCHEME, "", "/" + address, null).toString(); - } catch (URISyntaxException e) { - // It should not happen. - throw new RuntimeException(e); - } - } - - protected AbstractManagedChannelImplBuilder(SocketAddress directServerAddress, String authority) { - this.target = makeTargetStringForDirectAddress(directServerAddress); - this.directServerAddress = directServerAddress; - this.nameResolverFactory = new DirectAddressNameResolverFactory(directServerAddress, authority); - } - - @Override - public final T directExecutor() { - return executor(MoreExecutors.directExecutor()); - } - - @Override - public final T executor(Executor executor) { - if (executor != null) { - this.executorPool = new FixedObjectPool<>(executor); - } else { - this.executorPool = DEFAULT_EXECUTOR_POOL; - } - return thisT(); - } - - @Override - public final T offloadExecutor(Executor executor) { - if (executor != null) { - this.offloadExecutorPool = new FixedObjectPool<>(executor); - } else { - this.offloadExecutorPool = DEFAULT_EXECUTOR_POOL; - } - return thisT(); - } - - @Override - public final T intercept(List interceptors) { - this.interceptors.addAll(interceptors); - return thisT(); - } - - @Override - public final T intercept(ClientInterceptor... interceptors) { - return intercept(Arrays.asList(interceptors)); - } - - @Deprecated - @Override - public final T nameResolverFactory(NameResolver.Factory resolverFactory) { - Preconditions.checkState(directServerAddress == null, - "directServerAddress is set (%s), which forbids the use of NameResolverFactory", - directServerAddress); - if (resolverFactory != null) { - this.nameResolverFactory = resolverFactory; - } else { - this.nameResolverFactory = nameResolverRegistry.asFactory(); - } - return thisT(); - } - - @Override - public final T defaultLoadBalancingPolicy(String policy) { - Preconditions.checkState(directServerAddress == null, - "directServerAddress is set (%s), which forbids the use of load-balancing policy", - directServerAddress); - Preconditions.checkArgument(policy != null, "policy cannot be null"); - this.defaultLbPolicy = policy; - return thisT(); - } - - @Override - public final T enableFullStreamDecompression() { - this.fullStreamDecompression = true; - return thisT(); - } - - @Override - public final T decompressorRegistry(DecompressorRegistry registry) { - if (registry != null) { - this.decompressorRegistry = registry; - } else { - this.decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY; - } - return thisT(); - } - - @Override - public final T compressorRegistry(CompressorRegistry registry) { - if (registry != null) { - this.compressorRegistry = registry; - } else { - this.compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY; - } - return thisT(); - } - - @Override - public final T userAgent(@Nullable String userAgent) { - this.userAgent = userAgent; - return thisT(); - } - - @Override - public final T overrideAuthority(String authority) { - this.authorityOverride = checkAuthority(authority); - return thisT(); - } - - @Override - public final T idleTimeout(long value, TimeUnit unit) { - checkArgument(value > 0, "idle timeout is %s, but must be positive", value); - // We convert to the largest unit to avoid overflow - if (unit.toDays(value) >= IDLE_MODE_MAX_TIMEOUT_DAYS) { - // This disables idle mode - this.idleTimeoutMillis = ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE; - } else { - this.idleTimeoutMillis = Math.max(unit.toMillis(value), IDLE_MODE_MIN_TIMEOUT_MILLIS); - } - return thisT(); - } - - @Override - public final T maxRetryAttempts(int maxRetryAttempts) { - this.maxRetryAttempts = maxRetryAttempts; - return thisT(); - } - - @Override - public final T maxHedgedAttempts(int maxHedgedAttempts) { - this.maxHedgedAttempts = maxHedgedAttempts; - return thisT(); - } - - @Override - public final T retryBufferSize(long bytes) { - checkArgument(bytes > 0L, "retry buffer size must be positive"); - retryBufferSize = bytes; - return thisT(); - } - - @Override - public final T perRpcBufferLimit(long bytes) { - checkArgument(bytes > 0L, "per RPC buffer limit must be positive"); - perRpcBufferLimit = bytes; - return thisT(); - } - - @Override - public final T disableRetry() { - retryEnabled = false; - return thisT(); - } - - @Override - public final T enableRetry() { - retryEnabled = true; - statsEnabled = false; - tracingEnabled = false; - return thisT(); - } - - @Override - public final T setBinaryLog(BinaryLog binlog) { - this.binlog = binlog; - return thisT(); - } - - @Override - public T maxTraceEvents(int maxTraceEvents) { - checkArgument(maxTraceEvents >= 0, "maxTraceEvents must be non-negative"); - this.maxTraceEvents = maxTraceEvents; - return thisT(); - } - - @Override - public T proxyDetector(@Nullable ProxyDetector proxyDetector) { - this.proxyDetector = proxyDetector; - return thisT(); - } - - @Override - public T defaultServiceConfig(@Nullable Map serviceConfig) { - // TODO(notcarl): use real parsing - defaultServiceConfig = checkMapEntryTypes(serviceConfig); - return thisT(); - } - - @Nullable - private static Map checkMapEntryTypes(@Nullable Map map) { - if (map == null) { - return null; - } - // Not using ImmutableMap.Builder because of extra guava dependency for Android. - Map parsedMap = new LinkedHashMap<>(); - for (Map.Entry entry : map.entrySet()) { - checkArgument( - entry.getKey() instanceof String, - "The key of the entry '%s' is not of String type", entry); - - String key = (String) entry.getKey(); - Object value = entry.getValue(); - if (value == null) { - parsedMap.put(key, null); - } else if (value instanceof Map) { - parsedMap.put(key, checkMapEntryTypes((Map) value)); - } else if (value instanceof List) { - parsedMap.put(key, checkListEntryTypes((List) value)); - } else if (value instanceof String) { - parsedMap.put(key, value); - } else if (value instanceof Double) { - parsedMap.put(key, value); - } else if (value instanceof Boolean) { - parsedMap.put(key, value); - } else { - throw new IllegalArgumentException( - "The value of the map entry '" + entry + "' is of type '" + value.getClass() - + "', which is not supported"); - } - } - return Collections.unmodifiableMap(parsedMap); - } - - private static List checkListEntryTypes(List list) { - List parsedList = new ArrayList<>(list.size()); - for (Object value : list) { - if (value == null) { - parsedList.add(null); - } else if (value instanceof Map) { - parsedList.add(checkMapEntryTypes((Map) value)); - } else if (value instanceof List) { - parsedList.add(checkListEntryTypes((List) value)); - } else if (value instanceof String) { - parsedList.add(value); - } else if (value instanceof Double) { - parsedList.add(value); - } else if (value instanceof Boolean) { - parsedList.add(value); - } else { - throw new IllegalArgumentException( - "The entry '" + value + "' is of type '" + value.getClass() - + "', which is not supported"); - } - } - return Collections.unmodifiableList(parsedList); - } - - @Override - public T disableServiceConfigLookUp() { - this.lookUpServiceConfig = false; - return thisT(); - } - - /** - * Disable or enable stats features. Enabled by default. - * - *

For the current release, calling {@code setStatsEnabled(true)} may have a side effect that - * disables retry. - */ - protected void setStatsEnabled(boolean value) { - statsEnabled = value; - } - - /** - * Disable or enable stats recording for RPC upstarts. Effective only if {@link - * #setStatsEnabled} is set to true. Enabled by default. - */ - protected void setStatsRecordStartedRpcs(boolean value) { - recordStartedRpcs = value; - } - - /** - * Disable or enable stats recording for RPC completions. Effective only if {@link - * #setStatsEnabled} is set to true. Enabled by default. - */ - protected void setStatsRecordFinishedRpcs(boolean value) { - recordFinishedRpcs = value; - } - - /** - * Disable or enable real-time metrics recording. Effective only if {@link #setStatsEnabled} is - * set to true. Disabled by default. - */ - protected void setStatsRecordRealTimeMetrics(boolean value) { - recordRealTimeMetrics = value; - } - - /** - * Disable or enable tracing features. Enabled by default. - * - *

For the current release, calling {@code setTracingEnabled(true)} may have a side effect that - * disables retry. - */ - protected void setTracingEnabled(boolean value) { - tracingEnabled = value; - } - - @VisibleForTesting - final long getIdleTimeoutMillis() { - return idleTimeoutMillis; - } - - /** - * Verifies the authority is valid. This method exists as an escape hatch for putting in an - * authority that is valid, but would fail the default validation provided by this - * implementation. - */ - protected String checkAuthority(String authority) { - return GrpcUtil.checkAuthority(authority); - } - - @Override - public ManagedChannel build() { - return new ManagedChannelOrphanWrapper(new ManagedChannelImpl( - this, - buildTransportFactory(), - new ExponentialBackoffPolicy.Provider(), - SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR), - GrpcUtil.STOPWATCH_SUPPLIER, - getEffectiveInterceptors(), - TimeProvider.SYSTEM_TIME_PROVIDER)); - } - - // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know - // what should be the desired behavior for retry + stats/tracing. - // TODO(zdapeng): FIX IT - @VisibleForTesting - final List getEffectiveInterceptors() { - List effectiveInterceptors = - new ArrayList<>(this.interceptors); - temporarilyDisableRetry = false; - if (statsEnabled) { - temporarilyDisableRetry = true; - ClientInterceptor statsInterceptor = null; - try { - Class censusStatsAccessor = - Class.forName("io.grpc.census.InternalCensusStatsAccessor"); - Method getClientInterceptorMethod = - censusStatsAccessor.getDeclaredMethod( - "getClientInterceptor", - boolean.class, - boolean.class, - boolean.class); - statsInterceptor = - (ClientInterceptor) getClientInterceptorMethod - .invoke( - null, - recordStartedRpcs, - recordFinishedRpcs, - recordRealTimeMetrics); - } catch (ClassNotFoundException e) { - // Replace these separate catch statements with multicatch when Android min-API >= 19 - log.log(Level.FINE, "Unable to apply census stats", e); - } catch (NoSuchMethodException e) { - log.log(Level.FINE, "Unable to apply census stats", e); - } catch (IllegalAccessException e) { - log.log(Level.FINE, "Unable to apply census stats", e); - } catch (InvocationTargetException e) { - log.log(Level.FINE, "Unable to apply census stats", e); - } - if (statsInterceptor != null) { - // First interceptor runs last (see ClientInterceptors.intercept()), so that no - // other interceptor can override the tracer factory we set in CallOptions. - effectiveInterceptors.add(0, statsInterceptor); - } - } - if (tracingEnabled) { - temporarilyDisableRetry = true; - ClientInterceptor tracingInterceptor = null; - try { - Class censusTracingAccessor = - Class.forName("io.grpc.census.InternalCensusTracingAccessor"); - Method getClientInterceptroMethod = - censusTracingAccessor.getDeclaredMethod("getClientInterceptor"); - tracingInterceptor = (ClientInterceptor) getClientInterceptroMethod.invoke(null); - } catch (ClassNotFoundException e) { - // Replace these separate catch statements with multicatch when Android min-API >= 19 - log.log(Level.FINE, "Unable to apply census stats", e); - } catch (NoSuchMethodException e) { - log.log(Level.FINE, "Unable to apply census stats", e); - } catch (IllegalAccessException e) { - log.log(Level.FINE, "Unable to apply census stats", e); - } catch (InvocationTargetException e) { - log.log(Level.FINE, "Unable to apply census stats", e); - } - if (tracingInterceptor != null) { - effectiveInterceptors.add(0, tracingInterceptor); - } - } - return effectiveInterceptors; - } - - /** - * Subclasses should override this method to provide the {@link ClientTransportFactory} - * appropriate for this channel. This method is meant for Transport implementors and should not - * be used by normal users. - */ - protected abstract ClientTransportFactory buildTransportFactory(); - - /** - * Subclasses can override this method to provide a default port to {@link NameResolver} for use - * in cases where the target string doesn't include a port. The default implementation returns - * {@link GrpcUtil#DEFAULT_PORT_SSL}. - */ - protected int getDefaultPort() { - return GrpcUtil.DEFAULT_PORT_SSL; - } - - /** - * Returns a {@link NameResolver.Factory} for the channel. - */ - NameResolver.Factory getNameResolverFactory() { - if (authorityOverride == null) { - return nameResolverFactory; - } else { - return new OverrideAuthorityNameResolverFactory(nameResolverFactory, authorityOverride); - } - } - - private static class DirectAddressNameResolverFactory extends NameResolver.Factory { - final SocketAddress address; - final String authority; - - DirectAddressNameResolverFactory(SocketAddress address, String authority) { - this.address = address; - this.authority = authority; - } - - @Override - public NameResolver newNameResolver(URI notUsedUri, NameResolver.Args args) { - return new NameResolver() { - @Override - public String getServiceAuthority() { - return authority; - } - - @Override - public void start(Listener2 listener) { - listener.onResult( - ResolutionResult.newBuilder() - .setAddresses(Collections.singletonList(new EquivalentAddressGroup(address))) - .setAttributes(Attributes.EMPTY) - .build()); - } - - @Override - public void shutdown() {} - }; - } - - @Override - public String getDefaultScheme() { - return DIRECT_ADDRESS_SCHEME; - } - } - - /** - * Returns the correctly typed version of the builder. - */ - private T thisT() { - @SuppressWarnings("unchecked") - T thisT = (T) this; - return thisT; - } - - /** - * Returns the internal offload executor pool for offloading tasks. - */ - protected ObjectPool getOffloadExecutorPool() { - return this.offloadExecutorPool; - } -} diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java index 1bd42c04e8d..d2bc87cdde3 100644 --- a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java +++ b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java @@ -44,6 +44,7 @@ import io.grpc.Context; import io.grpc.DecompressorRegistry; import io.grpc.EquivalentAddressGroup; +import io.grpc.ForwardingChannelBuilder; import io.grpc.InternalChannelz; import io.grpc.InternalChannelz.ChannelStats; import io.grpc.InternalChannelz.ChannelTrace; @@ -72,6 +73,8 @@ import io.grpc.SynchronizationContext.ScheduledHandle; import io.grpc.internal.AutoConfiguredLoadBalancerFactory.AutoConfiguredLoadBalancer; import io.grpc.internal.ClientCallImpl.ClientStreamProvider; +import io.grpc.internal.ManagedChannelImplBuilder.FixedPortProvider; +import io.grpc.internal.ManagedChannelImplBuilder.UnsupportedClientTransportFactoryBuilder; import io.grpc.internal.ManagedChannelServiceConfig.MethodInfo; import io.grpc.internal.RetriableStream.ChannelBufferMeter; import io.grpc.internal.RetriableStream.Throttle; @@ -574,7 +577,7 @@ ClientStream newSubstream(ClientStreamTracer.Factory tracerFactory, Metadata new private final Rescheduler idleTimer; ManagedChannelImpl( - AbstractManagedChannelImplBuilder builder, + ManagedChannelImplBuilder builder, ClientTransportFactory clientTransportFactory, BackoffPolicy.Provider backoffPolicyProvider, ObjectPool balancerRpcExecutorPool, @@ -661,7 +664,7 @@ public void execute(Runnable command) { } else { checkArgument( builder.idleTimeoutMillis - >= AbstractManagedChannelImplBuilder.IDLE_MODE_MIN_TIMEOUT_MILLIS, + >= ManagedChannelImplBuilder.IDLE_MODE_MIN_TIMEOUT_MILLIS, "invalid idleTimeoutMillis %s", builder.idleTimeoutMillis); this.idleTimeoutMillis = builder.idleTimeoutMillis; } @@ -1446,28 +1449,27 @@ public void run() { @Override public ManagedChannelBuilder createResolvingOobChannelBuilder(String target) { final class ResolvingOobChannelBuilder - extends AbstractManagedChannelImplBuilder { - int defaultPort = -1; + extends ForwardingChannelBuilder { + private final ManagedChannelImplBuilder managedChannelImplBuilder; ResolvingOobChannelBuilder(String target) { - super(target); + managedChannelImplBuilder = new ManagedChannelImplBuilder(target, + new UnsupportedClientTransportFactoryBuilder(), + new FixedPortProvider(nameResolverArgs.getDefaultPort())); + managedChannelImplBuilder.executorPool = executorPool; + managedChannelImplBuilder.offloadExecutorPool = offloadExecutorHolder.pool; } @Override - public int getDefaultPort() { - return defaultPort; - } - - @Override - protected ClientTransportFactory buildTransportFactory() { - throw new UnsupportedOperationException(); + protected ManagedChannelBuilder delegate() { + return managedChannelImplBuilder; } @Override public ManagedChannel build() { // TODO(creamsoup) prevent main channel to shutdown if oob channel is not terminated return new ManagedChannelImpl( - this, + managedChannelImplBuilder, transportFactory, backoffPolicyProvider, balancerRpcExecutorPool, @@ -1479,17 +1481,15 @@ public ManagedChannel build() { checkState(!terminated, "Channel is terminated"); - ResolvingOobChannelBuilder builder = new ResolvingOobChannelBuilder(target); - builder.offloadExecutorPool = offloadExecutorHolder.pool; - builder.overrideAuthority(getAuthority()); @SuppressWarnings("deprecation") - ResolvingOobChannelBuilder unused = builder.nameResolverFactory(nameResolverFactory); - builder.executorPool = executorPool; - builder.maxTraceEvents = maxTraceEvents; - builder.proxyDetector = nameResolverArgs.getProxyDetector(); - builder.defaultPort = nameResolverArgs.getDefaultPort(); - builder.userAgent = userAgent; - return builder; + ResolvingOobChannelBuilder builder = new ResolvingOobChannelBuilder(target) + .nameResolverFactory(nameResolverFactory); + + return builder + .overrideAuthority(getAuthority()) + .maxTraceEvents(maxTraceEvents) + .proxyDetector(nameResolverArgs.getProxyDetector()) + .userAgent(userAgent); } @Override diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java b/core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java index d2807df200b..202055f9714 100644 --- a/core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java +++ b/core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java @@ -16,19 +16,148 @@ package io.grpc.internal; +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.MoreExecutors; +import io.grpc.Attributes; +import io.grpc.BinaryLog; +import io.grpc.ClientInterceptor; +import io.grpc.CompressorRegistry; +import io.grpc.DecompressorRegistry; +import io.grpc.EquivalentAddressGroup; +import io.grpc.InternalChannelz; +import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; +import io.grpc.NameResolver; +import io.grpc.NameResolverRegistry; +import io.grpc.ProxyDetector; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.SocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.annotation.Nullable; /** * Default managed channel builder, for usage in Transport implementations. */ public final class ManagedChannelImplBuilder - extends AbstractManagedChannelImplBuilder { + extends ManagedChannelBuilder { + private static final String DIRECT_ADDRESS_SCHEME = "directaddress"; + + private static final Logger log = Logger.getLogger(ManagedChannelImplBuilder.class.getName()); + + public static ManagedChannelBuilder forAddress(String name, int port) { + throw new UnsupportedOperationException( + "ClientTransportFactoryBuilder is required, use a constructor"); + } + + public static ManagedChannelBuilder forTarget(String target) { + throw new UnsupportedOperationException( + "ClientTransportFactoryBuilder is required, use a constructor"); + } + + /** + * An idle timeout larger than this would disable idle mode. + */ + @VisibleForTesting + static final long IDLE_MODE_MAX_TIMEOUT_DAYS = 30; + + /** + * The default idle timeout. + */ + @VisibleForTesting + static final long IDLE_MODE_DEFAULT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(30); + + /** + * An idle timeout smaller than this would be capped to it. + */ + static final long IDLE_MODE_MIN_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(1); + + private static final ObjectPool DEFAULT_EXECUTOR_POOL = + SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR); + + private static final DecompressorRegistry DEFAULT_DECOMPRESSOR_REGISTRY = + DecompressorRegistry.getDefaultInstance(); + + private static final CompressorRegistry DEFAULT_COMPRESSOR_REGISTRY = + CompressorRegistry.getDefaultInstance(); + + private static final long DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES = 1L << 24; // 16M + private static final long DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES = 1L << 20; // 1M + + ObjectPool executorPool = DEFAULT_EXECUTOR_POOL; + + ObjectPool offloadExecutorPool = DEFAULT_EXECUTOR_POOL; + + private final List interceptors = new ArrayList<>(); + final NameResolverRegistry nameResolverRegistry = NameResolverRegistry.getDefaultRegistry(); + + // Access via getter, which may perform authority override as needed + private NameResolver.Factory nameResolverFactory = nameResolverRegistry.asFactory(); + + final String target; + + @Nullable + private final SocketAddress directServerAddress; + + @Nullable + String userAgent; + + @Nullable + private String authorityOverride; + + String defaultLbPolicy = GrpcUtil.DEFAULT_LB_POLICY; + + boolean fullStreamDecompression; + + DecompressorRegistry decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY; + + CompressorRegistry compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY; + + long idleTimeoutMillis = IDLE_MODE_DEFAULT_TIMEOUT_MILLIS; + + int maxRetryAttempts = 5; + int maxHedgedAttempts = 5; + long retryBufferSize = DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES; + long perRpcBufferLimit = DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES; + boolean retryEnabled = false; // TODO(zdapeng): default to true + // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know + // what should be the desired behavior for retry + stats/tracing. + // TODO(zdapeng): delete me + boolean temporarilyDisableRetry; + + InternalChannelz channelz = InternalChannelz.instance(); + int maxTraceEvents; + + @Nullable + Map defaultServiceConfig; + boolean lookUpServiceConfig = true; + + @Nullable + BinaryLog binlog; + + @Nullable + ProxyDetector proxyDetector; private boolean authorityCheckerDisabled; + private boolean statsEnabled = true; + private boolean recordStartedRpcs = true; + private boolean recordFinishedRpcs = true; + private boolean recordRealTimeMetrics = false; + private boolean tracingEnabled = true; /** * An interface for Transport implementors to provide the {@link ClientTransportFactory} @@ -38,6 +167,17 @@ public interface ClientTransportFactoryBuilder { ClientTransportFactory buildClientTransportFactory(); } + /** + * Convenience ClientTransportFactoryBuilder, throws UnsupportedOperationException(). + */ + public static class UnsupportedClientTransportFactoryBuilder implements + ClientTransportFactoryBuilder { + @Override + public ClientTransportFactory buildClientTransportFactory() { + throw new UnsupportedOperationException(); + } + } + /** * An interface for Transport implementors to provide a default port to {@link * io.grpc.NameResolver} for use in cases where the target string doesn't include a port. The @@ -63,11 +203,11 @@ public int getDefaultPort() { } } - private final class ManagedChannelDefaultPortProvider implements + private static final class ManagedChannelDefaultPortProvider implements ChannelBuilderDefaultPortProvider { @Override public int getDefaultPort() { - return ManagedChannelImplBuilder.super.getDefaultPort(); + return GrpcUtil.DEFAULT_PORT_SSL; } } @@ -82,9 +222,10 @@ public int getDefaultPort() { public ManagedChannelImplBuilder(String target, ClientTransportFactoryBuilder clientTransportFactoryBuilder, @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) { - super(target); + this.target = Preconditions.checkNotNull(target, "target"); this.clientTransportFactoryBuilder = Preconditions .checkNotNull(clientTransportFactoryBuilder, "clientTransportFactoryBuilder"); + this.directServerAddress = null; if (channelBuilderDefaultPortProvider != null) { this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider; @@ -93,6 +234,21 @@ public ManagedChannelImplBuilder(String target, } } + /** + * Returns a target string for the SocketAddress. It is only used as a placeholder, because + * DirectAddressNameResolverFactory will not actually try to use it. However, it must be a valid + * URI. + */ + @VisibleForTesting + static String makeTargetStringForDirectAddress(SocketAddress address) { + try { + return new URI(DIRECT_ADDRESS_SCHEME, "", "/" + address, null).toString(); + } catch (URISyntaxException e) { + // It should not happen. + throw new RuntimeException(e); + } + } + /** * Creates a new managed channel builder with the given server address, authority string of the * channel. Transport implementors must provide client transport factory builder, and may set @@ -101,9 +257,11 @@ public ManagedChannelImplBuilder(String target, public ManagedChannelImplBuilder(SocketAddress directServerAddress, String authority, ClientTransportFactoryBuilder clientTransportFactoryBuilder, @Nullable ChannelBuilderDefaultPortProvider channelBuilderDefaultPortProvider) { - super(directServerAddress, authority); + this.target = makeTargetStringForDirectAddress(directServerAddress); this.clientTransportFactoryBuilder = Preconditions .checkNotNull(clientTransportFactoryBuilder, "clientTransportFactoryBuilder"); + this.directServerAddress = directServerAddress; + this.nameResolverFactory = new DirectAddressNameResolverFactory(directServerAddress, authority); if (channelBuilderDefaultPortProvider != null) { this.channelBuilderDefaultPortProvider = channelBuilderDefaultPortProvider; @@ -113,70 +271,461 @@ public ManagedChannelImplBuilder(SocketAddress directServerAddress, String autho } @Override - protected ClientTransportFactory buildTransportFactory() { - return clientTransportFactoryBuilder.buildClientTransportFactory(); + public ManagedChannelImplBuilder directExecutor() { + return executor(MoreExecutors.directExecutor()); } @Override - protected int getDefaultPort() { - return channelBuilderDefaultPortProvider.getDefaultPort(); + public ManagedChannelImplBuilder executor(Executor executor) { + if (executor != null) { + this.executorPool = new FixedObjectPool<>(executor); + } else { + this.executorPool = DEFAULT_EXECUTOR_POOL; + } + return this; } - /** Disable the check whether the authority is valid. */ - public ManagedChannelImplBuilder disableCheckAuthority() { - authorityCheckerDisabled = true; + @Override + public ManagedChannelImplBuilder offloadExecutor(Executor executor) { + if (executor != null) { + this.offloadExecutorPool = new FixedObjectPool<>(executor); + } else { + this.offloadExecutorPool = DEFAULT_EXECUTOR_POOL; + } return this; } - /** Enable previously disabled authority check. */ - public ManagedChannelImplBuilder enableCheckAuthority() { - authorityCheckerDisabled = false; + @Override + public ManagedChannelImplBuilder intercept(List interceptors) { + this.interceptors.addAll(interceptors); return this; } @Override - protected String checkAuthority(String authority) { - if (authorityCheckerDisabled) { - return authority; + public ManagedChannelImplBuilder intercept(ClientInterceptor... interceptors) { + return intercept(Arrays.asList(interceptors)); + } + + @Deprecated + @Override + public ManagedChannelImplBuilder nameResolverFactory(NameResolver.Factory resolverFactory) { + Preconditions.checkState(directServerAddress == null, + "directServerAddress is set (%s), which forbids the use of NameResolverFactory", + directServerAddress); + if (resolverFactory != null) { + this.nameResolverFactory = resolverFactory; + } else { + this.nameResolverFactory = nameResolverRegistry.asFactory(); } - return super.checkAuthority(authority); + return this; } @Override - public void setStatsEnabled(boolean value) { - super.setStatsEnabled(value); + public ManagedChannelImplBuilder defaultLoadBalancingPolicy(String policy) { + Preconditions.checkState(directServerAddress == null, + "directServerAddress is set (%s), which forbids the use of load-balancing policy", + directServerAddress); + Preconditions.checkArgument(policy != null, "policy cannot be null"); + this.defaultLbPolicy = policy; + return this; } @Override - public void setStatsRecordStartedRpcs(boolean value) { - super.setStatsRecordStartedRpcs(value); + public ManagedChannelImplBuilder enableFullStreamDecompression() { + this.fullStreamDecompression = true; + return this; } @Override - public void setStatsRecordFinishedRpcs(boolean value) { - super.setStatsRecordFinishedRpcs(value); + public ManagedChannelImplBuilder decompressorRegistry(DecompressorRegistry registry) { + if (registry != null) { + this.decompressorRegistry = registry; + } else { + this.decompressorRegistry = DEFAULT_DECOMPRESSOR_REGISTRY; + } + return this; } @Override - public void setStatsRecordRealTimeMetrics(boolean value) { - super.setStatsRecordRealTimeMetrics(value); + public ManagedChannelImplBuilder compressorRegistry(CompressorRegistry registry) { + if (registry != null) { + this.compressorRegistry = registry; + } else { + this.compressorRegistry = DEFAULT_COMPRESSOR_REGISTRY; + } + return this; + } + + @Override + public ManagedChannelImplBuilder userAgent(@Nullable String userAgent) { + this.userAgent = userAgent; + return this; + } + + @Override + public ManagedChannelImplBuilder overrideAuthority(String authority) { + this.authorityOverride = checkAuthority(authority); + return this; + } + + @Nullable + @VisibleForTesting + String getOverrideAuthority() { + return authorityOverride; + } + + @Override + public ManagedChannelImplBuilder idleTimeout(long value, TimeUnit unit) { + checkArgument(value > 0, "idle timeout is %s, but must be positive", value); + // We convert to the largest unit to avoid overflow + if (unit.toDays(value) >= IDLE_MODE_MAX_TIMEOUT_DAYS) { + // This disables idle mode + this.idleTimeoutMillis = ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE; + } else { + this.idleTimeoutMillis = Math.max(unit.toMillis(value), IDLE_MODE_MIN_TIMEOUT_MILLIS); + } + return this; + } + + @Override + public ManagedChannelImplBuilder maxRetryAttempts(int maxRetryAttempts) { + this.maxRetryAttempts = maxRetryAttempts; + return this; + } + + @Override + public ManagedChannelImplBuilder maxHedgedAttempts(int maxHedgedAttempts) { + this.maxHedgedAttempts = maxHedgedAttempts; + return this; + } + + @Override + public ManagedChannelImplBuilder retryBufferSize(long bytes) { + checkArgument(bytes > 0L, "retry buffer size must be positive"); + retryBufferSize = bytes; + return this; } @Override + public ManagedChannelImplBuilder perRpcBufferLimit(long bytes) { + checkArgument(bytes > 0L, "per RPC buffer limit must be positive"); + perRpcBufferLimit = bytes; + return this; + } + + @Override + public ManagedChannelImplBuilder disableRetry() { + retryEnabled = false; + return this; + } + + @Override + public ManagedChannelImplBuilder enableRetry() { + retryEnabled = true; + statsEnabled = false; + tracingEnabled = false; + return this; + } + + @Override + public ManagedChannelImplBuilder setBinaryLog(BinaryLog binlog) { + this.binlog = binlog; + return this; + } + + @Override + public ManagedChannelImplBuilder maxTraceEvents(int maxTraceEvents) { + checkArgument(maxTraceEvents >= 0, "maxTraceEvents must be non-negative"); + this.maxTraceEvents = maxTraceEvents; + return this; + } + + @Override + public ManagedChannelImplBuilder proxyDetector(@Nullable ProxyDetector proxyDetector) { + this.proxyDetector = proxyDetector; + return this; + } + + @Override + public ManagedChannelImplBuilder defaultServiceConfig(@Nullable Map serviceConfig) { + // TODO(notcarl): use real parsing + defaultServiceConfig = checkMapEntryTypes(serviceConfig); + return this; + } + + @Nullable + private static Map checkMapEntryTypes(@Nullable Map map) { + if (map == null) { + return null; + } + // Not using ImmutableMap.Builder because of extra guava dependency for Android. + Map parsedMap = new LinkedHashMap<>(); + for (Map.Entry entry : map.entrySet()) { + checkArgument( + entry.getKey() instanceof String, + "The key of the entry '%s' is not of String type", entry); + + String key = (String) entry.getKey(); + Object value = entry.getValue(); + if (value == null) { + parsedMap.put(key, null); + } else if (value instanceof Map) { + parsedMap.put(key, checkMapEntryTypes((Map) value)); + } else if (value instanceof List) { + parsedMap.put(key, checkListEntryTypes((List) value)); + } else if (value instanceof String) { + parsedMap.put(key, value); + } else if (value instanceof Double) { + parsedMap.put(key, value); + } else if (value instanceof Boolean) { + parsedMap.put(key, value); + } else { + throw new IllegalArgumentException( + "The value of the map entry '" + entry + "' is of type '" + value.getClass() + + "', which is not supported"); + } + } + return Collections.unmodifiableMap(parsedMap); + } + + private static List checkListEntryTypes(List list) { + List parsedList = new ArrayList<>(list.size()); + for (Object value : list) { + if (value == null) { + parsedList.add(null); + } else if (value instanceof Map) { + parsedList.add(checkMapEntryTypes((Map) value)); + } else if (value instanceof List) { + parsedList.add(checkListEntryTypes((List) value)); + } else if (value instanceof String) { + parsedList.add(value); + } else if (value instanceof Double) { + parsedList.add(value); + } else if (value instanceof Boolean) { + parsedList.add(value); + } else { + throw new IllegalArgumentException( + "The entry '" + value + "' is of type '" + value.getClass() + + "', which is not supported"); + } + } + return Collections.unmodifiableList(parsedList); + } + + @Override + public ManagedChannelImplBuilder disableServiceConfigLookUp() { + this.lookUpServiceConfig = false; + return this; + } + + /** + * Disable or enable stats features. Enabled by default. + * + *

For the current release, calling {@code setStatsEnabled(true)} may have a side effect that + * disables retry. + */ + public void setStatsEnabled(boolean value) { + statsEnabled = value; + } + + /** + * Disable or enable stats recording for RPC upstarts. Effective only if {@link + * #setStatsEnabled} is set to true. Enabled by default. + */ + public void setStatsRecordStartedRpcs(boolean value) { + recordStartedRpcs = value; + } + + /** + * Disable or enable stats recording for RPC completions. Effective only if {@link + * #setStatsEnabled} is set to true. Enabled by default. + */ + public void setStatsRecordFinishedRpcs(boolean value) { + recordFinishedRpcs = value; + } + + /** + * Disable or enable real-time metrics recording. Effective only if {@link #setStatsEnabled} is + * set to true. Disabled by default. + */ + public void setStatsRecordRealTimeMetrics(boolean value) { + recordRealTimeMetrics = value; + } + + /** + * Disable or enable tracing features. Enabled by default. + * + *

For the current release, calling {@code setTracingEnabled(true)} may have a side effect that + * disables retry. + */ public void setTracingEnabled(boolean value) { - super.setTracingEnabled(value); + tracingEnabled = value; + } + + /** + * Verifies the authority is valid. + */ + @VisibleForTesting + String checkAuthority(String authority) { + if (authorityCheckerDisabled) { + return authority; + } + return GrpcUtil.checkAuthority(authority); + } + + /** Disable the check whether the authority is valid. */ + public ManagedChannelImplBuilder disableCheckAuthority() { + authorityCheckerDisabled = true; + return this; + } + + /** Enable previously disabled authority check. */ + public ManagedChannelImplBuilder enableCheckAuthority() { + authorityCheckerDisabled = false; + return this; } @Override - public ObjectPool getOffloadExecutorPool() { - return super.getOffloadExecutorPool(); + public ManagedChannel build() { + return new ManagedChannelOrphanWrapper(new ManagedChannelImpl( + this, + clientTransportFactoryBuilder.buildClientTransportFactory(), + new ExponentialBackoffPolicy.Provider(), + SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR), + GrpcUtil.STOPWATCH_SUPPLIER, + getEffectiveInterceptors(), + TimeProvider.SYSTEM_TIME_PROVIDER)); } - public static ManagedChannelBuilder forAddress(String name, int port) { - throw new UnsupportedOperationException("ClientTransportFactoryBuilder is required"); + // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know + // what should be the desired behavior for retry + stats/tracing. + // TODO(zdapeng): FIX IT + @VisibleForTesting + List getEffectiveInterceptors() { + List effectiveInterceptors = + new ArrayList<>(this.interceptors); + temporarilyDisableRetry = false; + if (statsEnabled) { + temporarilyDisableRetry = true; + ClientInterceptor statsInterceptor = null; + try { + Class censusStatsAccessor = + Class.forName("io.grpc.census.InternalCensusStatsAccessor"); + Method getClientInterceptorMethod = + censusStatsAccessor.getDeclaredMethod( + "getClientInterceptor", + boolean.class, + boolean.class, + boolean.class); + statsInterceptor = + (ClientInterceptor) getClientInterceptorMethod + .invoke( + null, + recordStartedRpcs, + recordFinishedRpcs, + recordRealTimeMetrics); + } catch (ClassNotFoundException e) { + // Replace these separate catch statements with multicatch when Android min-API >= 19 + log.log(Level.FINE, "Unable to apply census stats", e); + } catch (NoSuchMethodException e) { + log.log(Level.FINE, "Unable to apply census stats", e); + } catch (IllegalAccessException e) { + log.log(Level.FINE, "Unable to apply census stats", e); + } catch (InvocationTargetException e) { + log.log(Level.FINE, "Unable to apply census stats", e); + } + if (statsInterceptor != null) { + // First interceptor runs last (see ClientInterceptors.intercept()), so that no + // other interceptor can override the tracer factory we set in CallOptions. + effectiveInterceptors.add(0, statsInterceptor); + } + } + if (tracingEnabled) { + temporarilyDisableRetry = true; + ClientInterceptor tracingInterceptor = null; + try { + Class censusTracingAccessor = + Class.forName("io.grpc.census.InternalCensusTracingAccessor"); + Method getClientInterceptroMethod = + censusTracingAccessor.getDeclaredMethod("getClientInterceptor"); + tracingInterceptor = (ClientInterceptor) getClientInterceptroMethod.invoke(null); + } catch (ClassNotFoundException e) { + // Replace these separate catch statements with multicatch when Android min-API >= 19 + log.log(Level.FINE, "Unable to apply census stats", e); + } catch (NoSuchMethodException e) { + log.log(Level.FINE, "Unable to apply census stats", e); + } catch (IllegalAccessException e) { + log.log(Level.FINE, "Unable to apply census stats", e); + } catch (InvocationTargetException e) { + log.log(Level.FINE, "Unable to apply census stats", e); + } + if (tracingInterceptor != null) { + effectiveInterceptors.add(0, tracingInterceptor); + } + } + return effectiveInterceptors; } - public static ManagedChannelBuilder forTarget(String target) { - throw new UnsupportedOperationException("ClientTransportFactoryBuilder is required"); + /** + * Returns a default port to {@link NameResolver} for use in cases where the target string doesn't + * include a port. The default implementation returns {@link GrpcUtil#DEFAULT_PORT_SSL}. + */ + int getDefaultPort() { + return channelBuilderDefaultPortProvider.getDefaultPort(); + } + + /** + * Returns a {@link NameResolver.Factory} for the channel. + */ + NameResolver.Factory getNameResolverFactory() { + if (authorityOverride == null) { + return nameResolverFactory; + } else { + return new OverrideAuthorityNameResolverFactory(nameResolverFactory, authorityOverride); + } + } + + private static class DirectAddressNameResolverFactory extends NameResolver.Factory { + final SocketAddress address; + final String authority; + + DirectAddressNameResolverFactory(SocketAddress address, String authority) { + this.address = address; + this.authority = authority; + } + + @Override + public NameResolver newNameResolver(URI notUsedUri, NameResolver.Args args) { + return new NameResolver() { + @Override + public String getServiceAuthority() { + return authority; + } + + @Override + public void start(Listener2 listener) { + listener.onResult( + ResolutionResult.newBuilder() + .setAddresses(Collections.singletonList(new EquivalentAddressGroup(address))) + .setAttributes(Attributes.EMPTY) + .build()); + } + + @Override + public void shutdown() {} + }; + } + + @Override + public String getDefaultScheme() { + return DIRECT_ADDRESS_SCHEME; + } + } + + /** + * Returns the internal offload executor pool for offloading tasks. + */ + public ObjectPool getOffloadExecutorPool() { + return this.offloadExecutorPool; } } diff --git a/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java b/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java deleted file mode 100644 index 2ac49c48ce3..00000000000 --- a/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java +++ /dev/null @@ -1,482 +0,0 @@ -/* - * Copyright 2016 The gRPC Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.grpc.internal; - -import static com.google.common.truth.Truth.assertThat; -import static junit.framework.TestCase.assertFalse; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; - -import com.google.common.util.concurrent.MoreExecutors; -import io.grpc.CallOptions; -import io.grpc.Channel; -import io.grpc.ClientCall; -import io.grpc.ClientInterceptor; -import io.grpc.CompressorRegistry; -import io.grpc.DecompressorRegistry; -import io.grpc.MethodDescriptor; -import io.grpc.NameResolver; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link AbstractManagedChannelImplBuilder}. */ -@RunWith(JUnit4.class) -public class AbstractManagedChannelImplBuilderTest { - - @Rule - public final ExpectedException thrown = ExpectedException.none(); - - private static final ClientInterceptor DUMMY_USER_INTERCEPTOR = - new ClientInterceptor() { - @Override - public ClientCall interceptCall( - MethodDescriptor method, CallOptions callOptions, Channel next) { - return next.newCall(method, callOptions); - } - }; - - private final Builder builder = new Builder("fake"); - private final Builder directAddressBuilder = new Builder(new SocketAddress(){}, "fake"); - - @Test - public void executor_default() { - assertNotNull(builder.executorPool); - } - - @Test - public void executor_normal() { - Executor executor = mock(Executor.class); - assertEquals(builder, builder.executor(executor)); - assertEquals(executor, builder.executorPool.getObject()); - } - - @Test - public void executor_null() { - ObjectPool defaultValue = builder.executorPool; - builder.executor(mock(Executor.class)); - assertEquals(builder, builder.executor(null)); - assertEquals(defaultValue, builder.executorPool); - } - - @Test - public void directExecutor() { - assertEquals(builder, builder.directExecutor()); - assertEquals(MoreExecutors.directExecutor(), builder.executorPool.getObject()); - } - - @Test - public void offloadExecutor_normal() { - Executor executor = mock(Executor.class); - assertEquals(builder, builder.offloadExecutor(executor)); - assertEquals(executor, builder.offloadExecutorPool.getObject()); - } - - @Test - public void offloadExecutor_null() { - ObjectPool defaultValue = builder.offloadExecutorPool; - builder.offloadExecutor(mock(Executor.class)); - assertEquals(builder, builder.offloadExecutor(null)); - assertEquals(defaultValue, builder.offloadExecutorPool); - } - - @Test - public void nameResolverFactory_default() { - assertNotNull(builder.getNameResolverFactory()); - } - - @Test - @SuppressWarnings("deprecation") - public void nameResolverFactory_normal() { - NameResolver.Factory nameResolverFactory = mock(NameResolver.Factory.class); - assertEquals(builder, builder.nameResolverFactory(nameResolverFactory)); - assertEquals(nameResolverFactory, builder.getNameResolverFactory()); - } - - @Test - @SuppressWarnings("deprecation") - public void nameResolverFactory_null() { - NameResolver.Factory defaultValue = builder.getNameResolverFactory(); - builder.nameResolverFactory(mock(NameResolver.Factory.class)); - assertEquals(builder, builder.nameResolverFactory(null)); - assertEquals(defaultValue, builder.getNameResolverFactory()); - } - - @Test(expected = IllegalStateException.class) - @SuppressWarnings("deprecation") - public void nameResolverFactory_notAllowedWithDirectAddress() { - directAddressBuilder.nameResolverFactory(mock(NameResolver.Factory.class)); - } - - @Test - public void defaultLoadBalancingPolicy_default() { - assertEquals("pick_first", builder.defaultLbPolicy); - } - - @Test - public void defaultLoadBalancingPolicy_normal() { - assertEquals(builder, builder.defaultLoadBalancingPolicy("magic_balancer")); - assertEquals("magic_balancer", builder.defaultLbPolicy); - } - - @Test(expected = IllegalArgumentException.class) - public void defaultLoadBalancingPolicy_null() { - builder.defaultLoadBalancingPolicy(null); - } - - @Test(expected = IllegalStateException.class) - public void defaultLoadBalancingPolicy_notAllowedWithDirectAddress() { - directAddressBuilder.defaultLoadBalancingPolicy("magic_balancer"); - } - - @Test - public void fullStreamDecompression_default() { - assertFalse(builder.fullStreamDecompression); - } - - @Test - public void fullStreamDecompression_enabled() { - assertEquals(builder, builder.enableFullStreamDecompression()); - assertTrue(builder.fullStreamDecompression); - } - - @Test - public void decompressorRegistry_default() { - assertNotNull(builder.decompressorRegistry); - } - - @Test - public void decompressorRegistry_normal() { - DecompressorRegistry decompressorRegistry = DecompressorRegistry.emptyInstance(); - assertNotEquals(decompressorRegistry, builder.decompressorRegistry); - assertEquals(builder, builder.decompressorRegistry(decompressorRegistry)); - assertEquals(decompressorRegistry, builder.decompressorRegistry); - } - - @Test - public void decompressorRegistry_null() { - DecompressorRegistry defaultValue = builder.decompressorRegistry; - assertEquals(builder, builder.decompressorRegistry(DecompressorRegistry.emptyInstance())); - assertNotEquals(defaultValue, builder.decompressorRegistry); - builder.decompressorRegistry(null); - assertEquals(defaultValue, builder.decompressorRegistry); - } - - @Test - public void compressorRegistry_default() { - assertNotNull(builder.compressorRegistry); - } - - @Test - public void compressorRegistry_normal() { - CompressorRegistry compressorRegistry = CompressorRegistry.newEmptyInstance(); - assertNotEquals(compressorRegistry, builder.compressorRegistry); - assertEquals(builder, builder.compressorRegistry(compressorRegistry)); - assertEquals(compressorRegistry, builder.compressorRegistry); - } - - @Test - public void compressorRegistry_null() { - CompressorRegistry defaultValue = builder.compressorRegistry; - builder.compressorRegistry(CompressorRegistry.newEmptyInstance()); - assertNotEquals(defaultValue, builder.compressorRegistry); - assertEquals(builder, builder.compressorRegistry(null)); - assertEquals(defaultValue, builder.compressorRegistry); - } - - @Test - public void userAgent_default() { - assertNull(builder.userAgent); - } - - @Test - public void userAgent_normal() { - String userAgent = "user-agent/1"; - assertEquals(builder, builder.userAgent(userAgent)); - assertEquals(userAgent, builder.userAgent); - } - - @Test - public void userAgent_null() { - assertEquals(builder, builder.userAgent(null)); - assertNull(builder.userAgent); - - builder.userAgent("user-agent/1"); - builder.userAgent(null); - assertNull(builder.userAgent); - } - - @Test - public void overrideAuthority_default() { - assertNull(builder.authorityOverride); - } - - @Test - public void overrideAuthority_normal() { - String overrideAuthority = "best-authority"; - assertEquals(builder, builder.overrideAuthority(overrideAuthority)); - assertEquals(overrideAuthority, builder.authorityOverride); - } - - @Test(expected = NullPointerException.class) - public void overrideAuthority_null() { - builder.overrideAuthority(null); - } - - @Test(expected = IllegalArgumentException.class) - public void overrideAuthority_invalid() { - builder.overrideAuthority("not_allowed"); - } - - @Test - public void overrideAuthority_getNameResolverFactory() { - assertNull(builder.authorityOverride); - assertFalse(builder.getNameResolverFactory() instanceof OverrideAuthorityNameResolverFactory); - builder.overrideAuthority("google.com"); - assertTrue(builder.getNameResolverFactory() instanceof OverrideAuthorityNameResolverFactory); - } - - @Test - public void makeTargetStringForDirectAddress_scopedIpv6() throws Exception { - InetSocketAddress address = new InetSocketAddress("0:0:0:0:0:0:0:0%0", 10005); - assertEquals("/0:0:0:0:0:0:0:0%0:10005", address.toString()); - String target = AbstractManagedChannelImplBuilder.makeTargetStringForDirectAddress(address); - URI uri = new URI(target); - assertEquals("directaddress:////0:0:0:0:0:0:0:0%250:10005", target); - assertEquals(target, uri.toString()); - } - - @Test - public void getEffectiveInterceptors_default() { - builder.intercept(DUMMY_USER_INTERCEPTOR); - List effectiveInterceptors = builder.getEffectiveInterceptors(); - assertEquals(3, effectiveInterceptors.size()); - assertThat(effectiveInterceptors.get(0).getClass().getName()) - .isEqualTo("io.grpc.census.CensusTracingModule$TracingClientInterceptor"); - assertThat(effectiveInterceptors.get(1).getClass().getName()) - .isEqualTo("io.grpc.census.CensusStatsModule$StatsClientInterceptor"); - assertThat(effectiveInterceptors.get(2)).isSameInstanceAs(DUMMY_USER_INTERCEPTOR); - } - - @Test - public void getEffectiveInterceptors_disableStats() { - builder.intercept(DUMMY_USER_INTERCEPTOR); - builder.setStatsEnabled(false); - List effectiveInterceptors = builder.getEffectiveInterceptors(); - assertEquals(2, effectiveInterceptors.size()); - assertThat(effectiveInterceptors.get(0).getClass().getName()) - .isEqualTo("io.grpc.census.CensusTracingModule$TracingClientInterceptor"); - assertThat(effectiveInterceptors.get(1)).isSameInstanceAs(DUMMY_USER_INTERCEPTOR); - } - - @Test - public void getEffectiveInterceptors_disableTracing() { - builder.intercept(DUMMY_USER_INTERCEPTOR); - builder.setTracingEnabled(false); - List effectiveInterceptors = builder.getEffectiveInterceptors(); - assertEquals(2, effectiveInterceptors.size()); - assertThat(effectiveInterceptors.get(0).getClass().getName()) - .isEqualTo("io.grpc.census.CensusStatsModule$StatsClientInterceptor"); - assertThat(effectiveInterceptors.get(1)).isSameInstanceAs(DUMMY_USER_INTERCEPTOR); - } - - @Test - public void getEffectiveInterceptors_disableBoth() { - builder.intercept(DUMMY_USER_INTERCEPTOR); - builder.setStatsEnabled(false); - builder.setTracingEnabled(false); - List effectiveInterceptors = builder.getEffectiveInterceptors(); - assertThat(effectiveInterceptors).containsExactly(DUMMY_USER_INTERCEPTOR); - } - - @Test - public void idleTimeout() { - assertEquals(AbstractManagedChannelImplBuilder.IDLE_MODE_DEFAULT_TIMEOUT_MILLIS, - builder.getIdleTimeoutMillis()); - - builder.idleTimeout(Long.MAX_VALUE, TimeUnit.DAYS); - assertEquals(ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE, builder.getIdleTimeoutMillis()); - - builder.idleTimeout(AbstractManagedChannelImplBuilder.IDLE_MODE_MAX_TIMEOUT_DAYS, - TimeUnit.DAYS); - assertEquals(ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE, builder.getIdleTimeoutMillis()); - - try { - builder.idleTimeout(0, TimeUnit.SECONDS); - fail("Should throw"); - } catch (IllegalArgumentException e) { - // expected - } - - builder.idleTimeout(1, TimeUnit.NANOSECONDS); - assertEquals(AbstractManagedChannelImplBuilder.IDLE_MODE_MIN_TIMEOUT_MILLIS, - builder.getIdleTimeoutMillis()); - - builder.idleTimeout(30, TimeUnit.SECONDS); - assertEquals(TimeUnit.SECONDS.toMillis(30), builder.getIdleTimeoutMillis()); - } - - @Test - public void maxRetryAttempts() { - assertEquals(5, builder.maxRetryAttempts); - - builder.maxRetryAttempts(3); - assertEquals(3, builder.maxRetryAttempts); - } - - @Test - public void maxHedgedAttempts() { - assertEquals(5, builder.maxHedgedAttempts); - - builder.maxHedgedAttempts(3); - assertEquals(3, builder.maxHedgedAttempts); - } - - @Test - public void retryBufferSize() { - assertEquals(1L << 24, builder.retryBufferSize); - - builder.retryBufferSize(3456L); - assertEquals(3456L, builder.retryBufferSize); - } - - @Test - public void perRpcBufferLimit() { - assertEquals(1L << 20, builder.perRpcBufferLimit); - - builder.perRpcBufferLimit(3456L); - assertEquals(3456L, builder.perRpcBufferLimit); - } - - @Test - public void retryBufferSizeInvalidArg() { - thrown.expect(IllegalArgumentException.class); - builder.retryBufferSize(0L); - } - - @Test - public void perRpcBufferLimitInvalidArg() { - thrown.expect(IllegalArgumentException.class); - builder.perRpcBufferLimit(0L); - } - - @Test - public void disableRetry() { - builder.enableRetry(); - assertTrue(builder.retryEnabled); - - builder.disableRetry(); - assertFalse(builder.retryEnabled); - - builder.enableRetry(); - assertTrue(builder.retryEnabled); - - builder.disableRetry(); - assertFalse(builder.retryEnabled); - } - - @Test - public void defaultServiceConfig_nullKey() { - Map config = new HashMap<>(); - config.put(null, "val"); - - thrown.expect(IllegalArgumentException.class); - builder.defaultServiceConfig(config); - } - - @Test - public void defaultServiceConfig_intKey() { - Map subConfig = new HashMap<>(); - subConfig.put(3, "val"); - Map config = new HashMap<>(); - config.put("key", subConfig); - - thrown.expect(IllegalArgumentException.class); - builder.defaultServiceConfig(config); - } - - @Test - public void defaultServiceConfig_intValue() { - Map config = new HashMap<>(); - config.put("key", 3); - - thrown.expect(IllegalArgumentException.class); - builder.defaultServiceConfig(config); - } - - @Test - public void defaultServiceConfig_nested() { - Map config = new HashMap<>(); - List list1 = new ArrayList<>(); - list1.add(123D); - list1.add(null); - list1.add(true); - list1.add("str"); - Map map2 = new HashMap<>(); - map2.put("key2", false); - map2.put("key3", null); - map2.put("key4", Collections.singletonList("v4")); - map2.put("key4", 3.14D); - map2.put("key5", new HashMap()); - list1.add(map2); - config.put("key1", list1); - - builder.defaultServiceConfig(config); - - assertThat(builder.defaultServiceConfig).containsExactlyEntriesIn(config); - } - - @Test - public void disableNameResolverServiceConfig() { - assertThat(builder.lookUpServiceConfig).isTrue(); - - builder.disableServiceConfigLookUp(); - assertThat(builder.lookUpServiceConfig).isFalse(); - } - - static class Builder extends AbstractManagedChannelImplBuilder { - Builder(String target) { - super(target); - } - - Builder(SocketAddress directServerAddress, String authority) { - super(directServerAddress, authority); - } - - @Override - protected ClientTransportFactory buildTransportFactory() { - throw new UnsupportedOperationException(); - } - } -} diff --git a/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java b/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java index 5d3f4cf4f14..dfe9d1973d5 100644 --- a/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java +++ b/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java @@ -16,14 +16,43 @@ package io.grpc.internal; +import static com.google.common.truth.Truth.assertThat; +import static junit.framework.TestCase.assertFalse; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.common.util.concurrent.MoreExecutors; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.CompressorRegistry; +import io.grpc.DecompressorRegistry; +import io.grpc.ManagedChannel; +import io.grpc.MethodDescriptor; +import io.grpc.NameResolver; import io.grpc.internal.ManagedChannelImplBuilder.ChannelBuilderDefaultPortProvider; import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder; import io.grpc.internal.ManagedChannelImplBuilder.FixedPortProvider; +import io.grpc.internal.ManagedChannelImplBuilder.UnsupportedClientTransportFactoryBuilder; +import io.grpc.testing.GrpcCleanupRule; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -41,56 +70,295 @@ public class ManagedChannelImplBuilderTest { private static final String DUMMY_TARGET = "fake-target"; private static final String DUMMY_AUTHORITY_VALID = "valid:1234"; private static final String DUMMY_AUTHORITY_INVALID = "[ : : 1]"; + private static final ClientInterceptor DUMMY_USER_INTERCEPTOR = + new ClientInterceptor() { + @Override + public ClientCall interceptCall( + MethodDescriptor method, CallOptions callOptions, Channel next) { + return next.newCall(method, callOptions); + } + }; @Rule public final MockitoRule mocks = MockitoJUnit.rule(); @Rule public final ExpectedException thrown = ExpectedException.none(); + @Rule public final GrpcCleanupRule grpcCleanupRule = new GrpcCleanupRule(); + @Mock private ClientTransportFactory mockClientTransportFactory; @Mock private ClientTransportFactoryBuilder mockClientTransportFactoryBuilder; - @Mock private ChannelBuilderDefaultPortProvider mockChannelBuilderDefaultPortProvider; + private ManagedChannelImplBuilder builder; + private ManagedChannelImplBuilder directAddressBuilder; + private final FakeClock clock = new FakeClock(); + @Before public void setUp() throws Exception { builder = new ManagedChannelImplBuilder( DUMMY_TARGET, - mockClientTransportFactoryBuilder, - mockChannelBuilderDefaultPortProvider); - } - - /** Ensure buildTransportFactory() delegates to the custom implementation. */ - @Test - public void buildTransportFactory() { - final ClientTransportFactory clientTransportFactory = mock(ClientTransportFactory.class); - when(mockClientTransportFactoryBuilder.buildClientTransportFactory()) - .thenReturn(clientTransportFactory); - assertEquals(clientTransportFactory, builder.buildTransportFactory()); - verify(mockClientTransportFactoryBuilder).buildClientTransportFactory(); + new UnsupportedClientTransportFactoryBuilder(), + new FixedPortProvider(DUMMY_PORT)); + directAddressBuilder = new ManagedChannelImplBuilder( + new SocketAddress() {}, + DUMMY_TARGET, + new UnsupportedClientTransportFactoryBuilder(), + new FixedPortProvider(DUMMY_PORT)); } /** Ensure getDefaultPort() returns default port when no custom implementation provided. */ @Test public void getDefaultPort_default() { - final ManagedChannelImplBuilder builderNoPortProvider = new ManagedChannelImplBuilder( - DUMMY_TARGET, mockClientTransportFactoryBuilder, null); - assertEquals(GrpcUtil.DEFAULT_PORT_SSL, builderNoPortProvider.getDefaultPort()); + builder = new ManagedChannelImplBuilder(DUMMY_TARGET, + new UnsupportedClientTransportFactoryBuilder(), null); + assertEquals(GrpcUtil.DEFAULT_PORT_SSL, builder.getDefaultPort()); } /** Ensure getDefaultPort() delegates to the custom implementation. */ @Test public void getDefaultPort_custom() { - when(mockChannelBuilderDefaultPortProvider.getDefaultPort()).thenReturn(DUMMY_PORT); - assertEquals(DUMMY_PORT, builder.getDefaultPort()); + int customPort = 43; + ChannelBuilderDefaultPortProvider mockChannelBuilderDefaultPortProvider = mock( + ChannelBuilderDefaultPortProvider.class); + when(mockChannelBuilderDefaultPortProvider.getDefaultPort()).thenReturn(customPort); + + builder = new ManagedChannelImplBuilder(DUMMY_TARGET, + new UnsupportedClientTransportFactoryBuilder(), + mockChannelBuilderDefaultPortProvider); + assertEquals(customPort, builder.getDefaultPort()); verify(mockChannelBuilderDefaultPortProvider).getDefaultPort(); } /** Test FixedPortProvider(int port). */ @Test public void getDefaultPort_fixedPortProvider() { - final ManagedChannelImplBuilder builderFixedPortProvider = new ManagedChannelImplBuilder( - DUMMY_TARGET, - mockClientTransportFactoryBuilder, - new FixedPortProvider(DUMMY_PORT)); - assertEquals(DUMMY_PORT, builderFixedPortProvider.getDefaultPort()); + int fixedPort = 43; + builder = new ManagedChannelImplBuilder(DUMMY_TARGET, + new UnsupportedClientTransportFactoryBuilder(), new FixedPortProvider(fixedPort)); + assertEquals(fixedPort, builder.getDefaultPort()); + } + + @Test + public void executor_default() { + assertNotNull(builder.executorPool); + } + + @Test + public void executor_normal() { + Executor executor = mock(Executor.class); + assertEquals(builder, builder.executor(executor)); + assertEquals(executor, builder.executorPool.getObject()); + } + + @Test + public void executor_null() { + ObjectPool defaultValue = builder.executorPool; + builder.executor(mock(Executor.class)); + assertEquals(builder, builder.executor(null)); + assertEquals(defaultValue, builder.executorPool); + } + + @Test + public void directExecutor() { + assertEquals(builder, builder.directExecutor()); + assertEquals(MoreExecutors.directExecutor(), builder.executorPool.getObject()); + } + + @Test + public void offloadExecutor_normal() { + Executor executor = mock(Executor.class); + assertEquals(builder, builder.offloadExecutor(executor)); + assertEquals(executor, builder.offloadExecutorPool.getObject()); + } + + @Test + public void offloadExecutor_null() { + ObjectPool defaultValue = builder.offloadExecutorPool; + builder.offloadExecutor(mock(Executor.class)); + assertEquals(builder, builder.offloadExecutor(null)); + assertEquals(defaultValue, builder.offloadExecutorPool); + } + + @Test + public void nameResolverFactory_default() { + assertNotNull(builder.getNameResolverFactory()); + } + + @Test + @SuppressWarnings("deprecation") + public void nameResolverFactory_normal() { + NameResolver.Factory nameResolverFactory = mock(NameResolver.Factory.class); + assertEquals(builder, builder.nameResolverFactory(nameResolverFactory)); + assertEquals(nameResolverFactory, builder.getNameResolverFactory()); + } + + @Test + @SuppressWarnings("deprecation") + public void nameResolverFactory_null() { + NameResolver.Factory defaultValue = builder.getNameResolverFactory(); + builder.nameResolverFactory(mock(NameResolver.Factory.class)); + assertEquals(builder, builder.nameResolverFactory(null)); + assertEquals(defaultValue, builder.getNameResolverFactory()); + } + + @Test(expected = IllegalStateException.class) + @SuppressWarnings("deprecation") + public void nameResolverFactory_notAllowedWithDirectAddress() { + directAddressBuilder.nameResolverFactory(mock(NameResolver.Factory.class)); + } + + @Test + public void defaultLoadBalancingPolicy_default() { + assertEquals("pick_first", builder.defaultLbPolicy); + } + + @Test + public void defaultLoadBalancingPolicy_normal() { + assertEquals(builder, builder.defaultLoadBalancingPolicy("magic_balancer")); + assertEquals("magic_balancer", builder.defaultLbPolicy); + } + + @Test(expected = IllegalArgumentException.class) + public void defaultLoadBalancingPolicy_null() { + builder.defaultLoadBalancingPolicy(null); + } + + @Test(expected = IllegalStateException.class) + public void defaultLoadBalancingPolicy_notAllowedWithDirectAddress() { + directAddressBuilder.defaultLoadBalancingPolicy("magic_balancer"); + } + + @Test + public void fullStreamDecompression_default() { + assertFalse(builder.fullStreamDecompression); + } + + @Test + public void fullStreamDecompression_enabled() { + assertEquals(builder, builder.enableFullStreamDecompression()); + assertTrue(builder.fullStreamDecompression); + } + + @Test + public void decompressorRegistry_default() { + assertNotNull(builder.decompressorRegistry); + } + + @Test + public void decompressorRegistry_normal() { + DecompressorRegistry decompressorRegistry = DecompressorRegistry.emptyInstance(); + assertNotEquals(decompressorRegistry, builder.decompressorRegistry); + assertEquals(builder, builder.decompressorRegistry(decompressorRegistry)); + assertEquals(decompressorRegistry, builder.decompressorRegistry); + } + + @Test + public void decompressorRegistry_null() { + DecompressorRegistry defaultValue = builder.decompressorRegistry; + assertEquals(builder, builder.decompressorRegistry(DecompressorRegistry.emptyInstance())); + assertNotEquals(defaultValue, builder.decompressorRegistry); + builder.decompressorRegistry(null); + assertEquals(defaultValue, builder.decompressorRegistry); + } + + @Test + public void compressorRegistry_default() { + assertNotNull(builder.compressorRegistry); + } + + @Test + public void compressorRegistry_normal() { + CompressorRegistry compressorRegistry = CompressorRegistry.newEmptyInstance(); + assertNotEquals(compressorRegistry, builder.compressorRegistry); + assertEquals(builder, builder.compressorRegistry(compressorRegistry)); + assertEquals(compressorRegistry, builder.compressorRegistry); + } + + @Test + public void compressorRegistry_null() { + CompressorRegistry defaultValue = builder.compressorRegistry; + builder.compressorRegistry(CompressorRegistry.newEmptyInstance()); + assertNotEquals(defaultValue, builder.compressorRegistry); + assertEquals(builder, builder.compressorRegistry(null)); + assertEquals(defaultValue, builder.compressorRegistry); + } + + @Test + public void userAgent_default() { + assertNull(builder.userAgent); + } + + @Test + public void userAgent_normal() { + String userAgent = "user-agent/1"; + assertEquals(builder, builder.userAgent(userAgent)); + assertEquals(userAgent, builder.userAgent); + } + + @Test + public void userAgent_null() { + assertEquals(builder, builder.userAgent(null)); + assertNull(builder.userAgent); + + builder.userAgent("user-agent/1"); + builder.userAgent(null); + assertNull(builder.userAgent); + } + + @Test + public void authorityIsReadable_default() { + when(mockClientTransportFactory.getScheduledExecutorService()) + .thenReturn(clock.getScheduledExecutorService()); + when(mockClientTransportFactoryBuilder.buildClientTransportFactory()) + .thenReturn(mockClientTransportFactory); + + builder = new ManagedChannelImplBuilder(DUMMY_AUTHORITY_VALID, + mockClientTransportFactoryBuilder, new FixedPortProvider(DUMMY_PORT)); + ManagedChannel channel = grpcCleanupRule.register(builder.build()); + assertEquals(DUMMY_AUTHORITY_VALID, channel.authority()); + } + + @Test + public void authorityIsReadable_overrideAuthority() { + String overrideAuthority = "best-authority"; + when(mockClientTransportFactory.getScheduledExecutorService()) + .thenReturn(clock.getScheduledExecutorService()); + when(mockClientTransportFactoryBuilder.buildClientTransportFactory()) + .thenReturn(mockClientTransportFactory); + + builder = new ManagedChannelImplBuilder(DUMMY_TARGET, + mockClientTransportFactoryBuilder, new FixedPortProvider(DUMMY_PORT)) + .overrideAuthority(overrideAuthority); + ManagedChannel channel = grpcCleanupRule.register(builder.build()); + assertEquals(overrideAuthority, channel.authority()); + } + + @Test + public void overrideAuthority_default() { + assertNull(builder.getOverrideAuthority()); + } + + @Test + public void overrideAuthority_normal() { + String overrideAuthority = "best-authority"; + assertEquals(builder, builder.overrideAuthority(overrideAuthority)); + assertEquals(overrideAuthority, builder.getOverrideAuthority()); + } + + @Test(expected = NullPointerException.class) + public void overrideAuthority_null() { + builder.overrideAuthority(null); + } + + @Test(expected = IllegalArgumentException.class) + public void overrideAuthority_invalid() { + builder.overrideAuthority("not_allowed"); + } + + @Test + public void overrideAuthority_getNameResolverFactory() { + assertNull(builder.getOverrideAuthority()); + assertFalse(builder.getNameResolverFactory() instanceof OverrideAuthorityNameResolverFactory); + builder.overrideAuthority("google.com"); + assertTrue(builder.getNameResolverFactory() instanceof OverrideAuthorityNameResolverFactory); } @Test @@ -132,4 +400,202 @@ public void disableCheckAuthority_invalidAuthorityFailed() { builder.disableCheckAuthority().enableCheckAuthority(); builder.checkAuthority(DUMMY_AUTHORITY_INVALID); } + + @Test + public void makeTargetStringForDirectAddress_scopedIpv6() throws Exception { + InetSocketAddress address = new InetSocketAddress("0:0:0:0:0:0:0:0%0", 10005); + assertEquals("/0:0:0:0:0:0:0:0%0:10005", address.toString()); + String target = ManagedChannelImplBuilder.makeTargetStringForDirectAddress(address); + URI uri = new URI(target); + assertEquals("directaddress:////0:0:0:0:0:0:0:0%250:10005", target); + assertEquals(target, uri.toString()); + } + + @Test + public void getEffectiveInterceptors_default() { + builder.intercept(DUMMY_USER_INTERCEPTOR); + List effectiveInterceptors = builder.getEffectiveInterceptors(); + assertEquals(3, effectiveInterceptors.size()); + assertThat(effectiveInterceptors.get(0).getClass().getName()) + .isEqualTo("io.grpc.census.CensusTracingModule$TracingClientInterceptor"); + assertThat(effectiveInterceptors.get(1).getClass().getName()) + .isEqualTo("io.grpc.census.CensusStatsModule$StatsClientInterceptor"); + assertThat(effectiveInterceptors.get(2)).isSameInstanceAs(DUMMY_USER_INTERCEPTOR); + } + + @Test + public void getEffectiveInterceptors_disableStats() { + builder.intercept(DUMMY_USER_INTERCEPTOR); + builder.setStatsEnabled(false); + List effectiveInterceptors = builder.getEffectiveInterceptors(); + assertEquals(2, effectiveInterceptors.size()); + assertThat(effectiveInterceptors.get(0).getClass().getName()) + .isEqualTo("io.grpc.census.CensusTracingModule$TracingClientInterceptor"); + assertThat(effectiveInterceptors.get(1)).isSameInstanceAs(DUMMY_USER_INTERCEPTOR); + } + + @Test + public void getEffectiveInterceptors_disableTracing() { + builder.intercept(DUMMY_USER_INTERCEPTOR); + builder.setTracingEnabled(false); + List effectiveInterceptors = builder.getEffectiveInterceptors(); + assertEquals(2, effectiveInterceptors.size()); + assertThat(effectiveInterceptors.get(0).getClass().getName()) + .isEqualTo("io.grpc.census.CensusStatsModule$StatsClientInterceptor"); + assertThat(effectiveInterceptors.get(1)).isSameInstanceAs(DUMMY_USER_INTERCEPTOR); + } + + @Test + public void getEffectiveInterceptors_disableBoth() { + builder.intercept(DUMMY_USER_INTERCEPTOR); + builder.setStatsEnabled(false); + builder.setTracingEnabled(false); + List effectiveInterceptors = builder.getEffectiveInterceptors(); + assertThat(effectiveInterceptors).containsExactly(DUMMY_USER_INTERCEPTOR); + } + + @Test + public void idleTimeout() { + assertEquals(ManagedChannelImplBuilder.IDLE_MODE_DEFAULT_TIMEOUT_MILLIS, + builder.idleTimeoutMillis); + + builder.idleTimeout(Long.MAX_VALUE, TimeUnit.DAYS); + assertEquals(ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE, builder.idleTimeoutMillis); + + builder.idleTimeout(ManagedChannelImplBuilder.IDLE_MODE_MAX_TIMEOUT_DAYS, + TimeUnit.DAYS); + assertEquals(ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE, builder.idleTimeoutMillis); + + try { + builder.idleTimeout(0, TimeUnit.SECONDS); + fail("Should throw"); + } catch (IllegalArgumentException e) { + // expected + } + + builder.idleTimeout(1, TimeUnit.NANOSECONDS); + assertEquals(ManagedChannelImplBuilder.IDLE_MODE_MIN_TIMEOUT_MILLIS, + builder.idleTimeoutMillis); + + builder.idleTimeout(30, TimeUnit.SECONDS); + assertEquals(TimeUnit.SECONDS.toMillis(30), builder.idleTimeoutMillis); + } + + @Test + public void maxRetryAttempts() { + assertEquals(5, builder.maxRetryAttempts); + + builder.maxRetryAttempts(3); + assertEquals(3, builder.maxRetryAttempts); + } + + @Test + public void maxHedgedAttempts() { + assertEquals(5, builder.maxHedgedAttempts); + + builder.maxHedgedAttempts(3); + assertEquals(3, builder.maxHedgedAttempts); + } + + @Test + public void retryBufferSize() { + assertEquals(1L << 24, builder.retryBufferSize); + + builder.retryBufferSize(3456L); + assertEquals(3456L, builder.retryBufferSize); + } + + @Test + public void perRpcBufferLimit() { + assertEquals(1L << 20, builder.perRpcBufferLimit); + + builder.perRpcBufferLimit(3456L); + assertEquals(3456L, builder.perRpcBufferLimit); + } + + @Test + public void retryBufferSizeInvalidArg() { + thrown.expect(IllegalArgumentException.class); + builder.retryBufferSize(0L); + } + + @Test + public void perRpcBufferLimitInvalidArg() { + thrown.expect(IllegalArgumentException.class); + builder.perRpcBufferLimit(0L); + } + + @Test + public void disableRetry() { + builder.enableRetry(); + assertTrue(builder.retryEnabled); + + builder.disableRetry(); + assertFalse(builder.retryEnabled); + + builder.enableRetry(); + assertTrue(builder.retryEnabled); + + builder.disableRetry(); + assertFalse(builder.retryEnabled); + } + + @Test + public void defaultServiceConfig_nullKey() { + Map config = new HashMap<>(); + config.put(null, "val"); + + thrown.expect(IllegalArgumentException.class); + builder.defaultServiceConfig(config); + } + + @Test + public void defaultServiceConfig_intKey() { + Map subConfig = new HashMap<>(); + subConfig.put(3, "val"); + Map config = new HashMap<>(); + config.put("key", subConfig); + + thrown.expect(IllegalArgumentException.class); + builder.defaultServiceConfig(config); + } + + @Test + public void defaultServiceConfig_intValue() { + Map config = new HashMap<>(); + config.put("key", 3); + + thrown.expect(IllegalArgumentException.class); + builder.defaultServiceConfig(config); + } + + @Test + public void defaultServiceConfig_nested() { + Map config = new HashMap<>(); + List list1 = new ArrayList<>(); + list1.add(123D); + list1.add(null); + list1.add(true); + list1.add("str"); + Map map2 = new HashMap<>(); + map2.put("key2", false); + map2.put("key3", null); + map2.put("key4", Collections.singletonList("v4")); + map2.put("key4", 3.14D); + map2.put("key5", new HashMap()); + list1.add(map2); + config.put("key1", list1); + + builder.defaultServiceConfig(config); + + assertThat(builder.defaultServiceConfig).containsExactlyEntriesIn(config); + } + + @Test + public void disableNameResolverServiceConfig() { + assertThat(builder.lookUpServiceConfig).isTrue(); + + builder.disableServiceConfigLookUp(); + assertThat(builder.lookUpServiceConfig).isFalse(); + } } diff --git a/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java b/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java index c551d26449c..a2a2925c4f3 100644 --- a/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java +++ b/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java @@ -62,7 +62,7 @@ import io.grpc.Status; import io.grpc.StringMarshaller; import io.grpc.internal.FakeClock.ScheduledTask; -import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder; +import io.grpc.internal.ManagedChannelImplBuilder.UnsupportedClientTransportFactoryBuilder; import io.grpc.internal.TestUtils.MockClientTransportInfo; import java.net.SocketAddress; import java.net.URI; @@ -161,12 +161,7 @@ public void setUp() { .thenReturn(timer.getScheduledExecutorService()); ManagedChannelImplBuilder builder = new ManagedChannelImplBuilder("fake://target", - new ClientTransportFactoryBuilder() { - @Override public ClientTransportFactory buildClientTransportFactory() { - throw new UnsupportedOperationException(); - } - }, - null); + new UnsupportedClientTransportFactoryBuilder(), null); builder .nameResolverFactory(mockNameResolverFactory) diff --git a/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java b/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java index cd14f409ca3..2931f4deb51 100644 --- a/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java +++ b/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java @@ -109,6 +109,7 @@ import io.grpc.internal.ManagedChannelImpl.ScParser; import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder; import io.grpc.internal.ManagedChannelImplBuilder.FixedPortProvider; +import io.grpc.internal.ManagedChannelImplBuilder.UnsupportedClientTransportFactoryBuilder; import io.grpc.internal.ServiceConfigUtil.PolicySelection; import io.grpc.internal.TestUtils.MockClientTransportInfo; import io.grpc.stub.ClientCalls; @@ -328,13 +329,8 @@ public void setUp() throws Exception { .thenReturn(balancerRpcExecutor.getScheduledExecutorService()); channelBuilder = new ManagedChannelImplBuilder(TARGET, - new ClientTransportFactoryBuilder() { - @Override - public ClientTransportFactory buildClientTransportFactory() { - throw new UnsupportedOperationException(); - } - }, - new FixedPortProvider(DEFAULT_PORT)); + new UnsupportedClientTransportFactoryBuilder(), new FixedPortProvider(DEFAULT_PORT)); + channelBuilder .nameResolverFactory(new FakeNameResolverFactory.Builder(expectedUri).build()) .defaultLoadBalancingPolicy(MOCK_POLICY_NAME) diff --git a/core/src/test/java/io/grpc/internal/ServiceConfigErrorHandlingTest.java b/core/src/test/java/io/grpc/internal/ServiceConfigErrorHandlingTest.java index 49f094a6adb..16c6f3bf302 100644 --- a/core/src/test/java/io/grpc/internal/ServiceConfigErrorHandlingTest.java +++ b/core/src/test/java/io/grpc/internal/ServiceConfigErrorHandlingTest.java @@ -46,8 +46,8 @@ import io.grpc.NameResolver; import io.grpc.NameResolver.ConfigOrError; import io.grpc.Status; -import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder; import io.grpc.internal.ManagedChannelImplBuilder.FixedPortProvider; +import io.grpc.internal.ManagedChannelImplBuilder.UnsupportedClientTransportFactoryBuilder; import java.net.SocketAddress; import java.net.URI; import java.util.ArrayList; @@ -200,13 +200,7 @@ public void setUp() throws Exception { when(executorPool.getObject()).thenReturn(executor.getScheduledExecutorService()); channelBuilder = new ManagedChannelImplBuilder(TARGET, - new ClientTransportFactoryBuilder() { - @Override - public ClientTransportFactory buildClientTransportFactory() { - throw new UnsupportedOperationException(); - } - }, - new FixedPortProvider(DEFAULT_PORT)); + new UnsupportedClientTransportFactoryBuilder(), new FixedPortProvider(DEFAULT_PORT)); channelBuilder .nameResolverFactory(new FakeNameResolverFactory.Builder(expectedUri).build())