diff --git a/core/src/main/java/io/grpc/internal/DnsNameResolver.java b/core/src/main/java/io/grpc/internal/DnsNameResolver.java index 1f060b3014..fbd5762afe 100644 --- a/core/src/main/java/io/grpc/internal/DnsNameResolver.java +++ b/core/src/main/java/io/grpc/internal/DnsNameResolver.java @@ -98,6 +98,8 @@ final class DnsNameResolver extends NameResolver { *

Default value is -1 (cache forever) if security manager is installed. If security manager is * not installed, the ttl value is {@code null} which falls back to {@link * #DEFAULT_NETWORK_CACHE_TTL_SECONDS gRPC default value}. + * + *

For android, gRPC doesn't attempt to cache; this property value will be ignored. */ @VisibleForTesting static final String NETWORKADDRESS_CACHE_TTL_PROPERTY = "networkaddress.cache.ttl"; @@ -143,7 +145,7 @@ final class DnsNameResolver extends NameResolver { DnsNameResolver(@Nullable String nsAuthority, String name, Attributes params, Resource executorResource, ProxyDetector proxyDetector, - Stopwatch stopwatch) { + Stopwatch stopwatch, boolean isAndroid) { // TODO: if a DNS server is provided as nsAuthority, use it. // https://www.captechconsulting.com/blogs/accessing-the-dusty-corners-of-dns-with-java this.executorResource = executorResource; @@ -166,7 +168,7 @@ final class DnsNameResolver extends NameResolver { port = nameUri.getPort(); } this.proxyDetector = proxyDetector; - this.resolveRunnable = new Resolve(this, stopwatch); + this.resolveRunnable = new Resolve(this, stopwatch, getNetworkAddressCacheTtlNanos(isAndroid)); } @Override @@ -196,10 +198,10 @@ final class DnsNameResolver extends NameResolver { private final long cacheTtlNanos; private ResolutionResults cachedResolutionResults = null; - Resolve(DnsNameResolver resolver, Stopwatch stopwatch) { + Resolve(DnsNameResolver resolver, Stopwatch stopwatch, long cacheTtlNanos) { this.resolver = resolver; this.stopwatch = Preconditions.checkNotNull(stopwatch, "stopwatch"); - this.cacheTtlNanos = getNetworkAddressCacheTtlNanos(); + this.cacheTtlNanos = cacheTtlNanos; } @Override @@ -230,23 +232,6 @@ final class DnsNameResolver extends NameResolver { || (cacheTtlNanos > 0 && stopwatch.elapsed(TimeUnit.NANOSECONDS) > cacheTtlNanos); } - /** Returns value of network address cache ttl property. */ - private static long getNetworkAddressCacheTtlNanos() { - String cacheTtlPropertyValue = System.getProperty(NETWORKADDRESS_CACHE_TTL_PROPERTY); - long cacheTtl = DEFAULT_NETWORK_CACHE_TTL_SECONDS; - if (cacheTtlPropertyValue != null) { - try { - cacheTtl = Long.parseLong(cacheTtlPropertyValue); - } catch (NumberFormatException e) { - logger.log( - Level.WARNING, - "Property({0}) valid is not valid number format({1}), fall back to default({2})", - new Object[] {NETWORKADDRESS_CACHE_TTL_PROPERTY, cacheTtlPropertyValue, cacheTtl}); - } - } - return cacheTtl > 0 ? TimeUnit.SECONDS.toNanos(cacheTtl) : cacheTtl; - } - @VisibleForTesting void resolveInternal(Listener savedListener) { InetSocketAddress destination = @@ -483,6 +468,31 @@ final class DnsNameResolver extends NameResolver { ServiceConfigUtil.getList(serviceConfigChoice, SERVICE_CONFIG_CHOICE_CLIENT_HOSTNAME_KEY)); } + /** + * Returns value of network address cache ttl property if not Android environment. For android, + * DnsNameResolver does not cache the dns lookup result. + */ + private static long getNetworkAddressCacheTtlNanos(boolean isAndroid) { + if (isAndroid) { + // on Android, ignore dns cache. + return 0; + } + + String cacheTtlPropertyValue = System.getProperty(NETWORKADDRESS_CACHE_TTL_PROPERTY); + long cacheTtl = DEFAULT_NETWORK_CACHE_TTL_SECONDS; + if (cacheTtlPropertyValue != null) { + try { + cacheTtl = Long.parseLong(cacheTtlPropertyValue); + } catch (NumberFormatException e) { + logger.log( + Level.WARNING, + "Property({0}) valid is not valid number format({1}), fall back to default({2})", + new Object[] {NETWORKADDRESS_CACHE_TTL_PROPERTY, cacheTtlPropertyValue, cacheTtl}); + } + } + return cacheTtl > 0 ? TimeUnit.SECONDS.toNanos(cacheTtl) : cacheTtl; + } + /** * Determines if a given Service Config choice applies, and if so, returns it. * diff --git a/core/src/main/java/io/grpc/internal/DnsNameResolverProvider.java b/core/src/main/java/io/grpc/internal/DnsNameResolverProvider.java index d0db539d41..eda7754477 100644 --- a/core/src/main/java/io/grpc/internal/DnsNameResolverProvider.java +++ b/core/src/main/java/io/grpc/internal/DnsNameResolverProvider.java @@ -19,6 +19,7 @@ package io.grpc.internal; import com.google.common.base.Preconditions; import com.google.common.base.Stopwatch; import io.grpc.Attributes; +import io.grpc.InternalServiceProviders; import io.grpc.NameResolverProvider; import java.net.URI; @@ -54,7 +55,8 @@ public final class DnsNameResolverProvider extends NameResolverProvider { params, GrpcUtil.SHARED_CHANNEL_EXECUTOR, GrpcUtil.getDefaultProxyDetector(), - Stopwatch.createUnstarted()); + Stopwatch.createUnstarted(), + InternalServiceProviders.isAndroid(getClass().getClassLoader())); } else { return null; } diff --git a/core/src/test/java/io/grpc/internal/DnsNameResolverTest.java b/core/src/test/java/io/grpc/internal/DnsNameResolverTest.java index 5a34520bc0..a3aa7315cd 100644 --- a/core/src/test/java/io/grpc/internal/DnsNameResolverTest.java +++ b/core/src/test/java/io/grpc/internal/DnsNameResolverTest.java @@ -118,18 +118,34 @@ public class DnsNameResolverTest { return newResolver(name, port, GrpcUtil.NOOP_PROXY_DETECTOR, Stopwatch.createUnstarted()); } + private DnsNameResolver newResolver(String name, int port, boolean isAndroid) { + return + newResolver( + name, port, GrpcUtil.NOOP_PROXY_DETECTOR, Stopwatch.createUnstarted(), isAndroid); + } + private DnsNameResolver newResolver( String name, int port, ProxyDetector proxyDetector, Stopwatch stopwatch) { + return newResolver(name, port, proxyDetector, stopwatch, false); + } + + private DnsNameResolver newResolver( + String name, + int port, + ProxyDetector proxyDetector, + Stopwatch stopwatch, + boolean isAndroid) { DnsNameResolver dnsResolver = new DnsNameResolver( null, name, Attributes.newBuilder().set(NameResolver.Factory.PARAMS_DEFAULT_PORT, port).build(), fakeExecutorResource, proxyDetector, - stopwatch); + stopwatch, + isAndroid); return dnsResolver; } @@ -200,14 +216,30 @@ public class DnsNameResolverTest { } } + @Test + public void resolve_androidIgnoresPropertyValue() throws Exception { + System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, Long.toString(2)); + resolveNeverCache(true); + } + + @Test + public void resolve_androidIgnoresPropertyValueCacheForever() throws Exception { + System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, Long.toString(-1)); + resolveNeverCache(true); + } + @Test public void resolve_neverCache() throws Exception { System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, "0"); + resolveNeverCache(false); + } + + private void resolveNeverCache(boolean isAndroid) throws Exception { final List answer1 = createAddressList(2); final List answer2 = createAddressList(1); String name = "foo.googleapis.com"; - DnsNameResolver resolver = newResolver(name, 81); + DnsNameResolver resolver = newResolver(name, 81, isAndroid); AddressResolver mockResolver = mock(AddressResolver.class); when(mockResolver.resolveAddress(Matchers.anyString())).thenReturn(answer1).thenReturn(answer2); resolver.setAddressResolver(mockResolver); @@ -241,7 +273,7 @@ public class DnsNameResolverTest { } }); - new DnsNameResolver.Resolve(nrf, Stopwatch.createUnstarted()).resolveInternal(mockListener); + new DnsNameResolver.Resolve(nrf, Stopwatch.createUnstarted(), 0).resolveInternal(mockListener); ArgumentCaptor ac = ArgumentCaptor.forClass(Status.class); verify(mockListener).onError(ac.capture());