diff --git a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java index f82f571511..f384360c34 100644 --- a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java +++ b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java @@ -42,10 +42,6 @@ import java.util.concurrent.atomic.AtomicLong; */ public final class MacOSDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider { - // Fallback provider - private static final DnsServerAddressStreamProvider DEFAULT_PROVIDER = - DnsServerAddressStreamProviders.platformDefault(); - private static final Throwable UNAVAILABILITY_CAUSE; private static final InternalLogger logger = @@ -113,7 +109,7 @@ public final class MacOSDnsServerAddressStreamProvider implements DnsServerAddre if (resolvers == null || resolvers.length == 0) { return Collections.emptyMap(); } - Map resolverMap = new HashMap(resolvers.length); + Map resolverMap = new HashMap<>(resolvers.length); for (DnsResolver resolver: resolvers) { // Skip mdns if ("mdns".equalsIgnoreCase(resolver.options())) { @@ -167,7 +163,7 @@ public final class MacOSDnsServerAddressStreamProvider implements DnsServerAddre if (addresses != null) { return addresses.stream(); } - return DEFAULT_PROVIDER.nameServerAddressStream(originalHostname); + return DnsServerAddressStreamProviders.unixDefault().nameServerAddressStream(originalHostname); } DnsServerAddresses addresses = resolverMap.get(hostname); diff --git a/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java b/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java index aafec23727..424716f939 100644 --- a/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java +++ b/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java @@ -17,6 +17,8 @@ package io.netty.resolver.dns.macos; import io.netty.resolver.dns.DnsServerAddressStream; import io.netty.resolver.dns.DnsServerAddressStreamProvider; +import io.netty.resolver.dns.DnsServerAddressStreamProviders; +import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Assume; import org.junit.BeforeClass; @@ -30,7 +32,7 @@ public class MacOSDnsServerAddressStreamProviderTest { } @Test - public void test() { + public void testStream() { DnsServerAddressStreamProvider provider = new MacOSDnsServerAddressStreamProvider(); DnsServerAddressStream stream = provider.nameServerAddressStream("netty.io"); Assert.assertNotNull(stream); @@ -40,4 +42,11 @@ public class MacOSDnsServerAddressStreamProviderTest { Assert.assertNotEquals(0, stream.next().getPort()); } } + + @Test + public void testDefaultUseCorrectInstance() { + Assert.assertThat(DnsServerAddressStreamProviders.platformDefault(), + Matchers.instanceOf(MacOSDnsServerAddressStreamProvider.class)); + } + } diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java index 2983ccbad9..c31fca1373 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java @@ -16,7 +16,14 @@ package io.netty.resolver.dns; import io.netty.util.internal.PlatformDependent; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; @@ -24,37 +31,46 @@ import java.util.concurrent.atomic.AtomicLong; * Utility methods related to {@link DnsServerAddressStreamProvider}. */ public final class DnsServerAddressStreamProviders { - // We use 5 minutes which is the same as what OpenJDK is using in sun.net.dns.ResolverConfigurationImpl. - private static final long REFRESH_INTERVAL = TimeUnit.MINUTES.toNanos(5); - // TODO(scott): how is this done on Windows? This may require a JNI call to GetNetworkParams - // https://msdn.microsoft.com/en-us/library/aa365968(VS.85).aspx. - private static final DnsServerAddressStreamProvider DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER = - new DnsServerAddressStreamProvider() { - private volatile DnsServerAddressStreamProvider currentProvider = provider(); - private final AtomicLong lastRefresh = new AtomicLong(System.nanoTime()); + private static final InternalLogger LOGGER = + InternalLoggerFactory.getInstance(DnsServerAddressStreamProviders.class); + private static final Constructor STREAM_PROVIDER_CONSTRUCTOR; - @Override - public DnsServerAddressStream nameServerAddressStream(String hostname) { - long last = lastRefresh.get(); - DnsServerAddressStreamProvider current = currentProvider; - if (System.nanoTime() - last > REFRESH_INTERVAL) { - // This is slightly racy which means it will be possible still use the old configuration for a small - // amount of time, but that's ok. - if (lastRefresh.compareAndSet(last, System.nanoTime())) { - current = currentProvider = provider(); + static { + Constructor constructor = null; + if (PlatformDependent.isOsx()) { + try { + // As MacOSDnsServerAddressStreamProvider is contained in another jar which depends on this jar + // we use reflection to use it if its on the classpath. + Object maybeProvider = AccessController.doPrivileged((PrivilegedAction) () -> { + try { + return Class.forName( + "io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider", + true, + DnsServerAddressStreamProviders.class.getClassLoader()); + } catch (Throwable cause) { + return cause; + } + }); + if (maybeProvider instanceof Class) { + @SuppressWarnings("unchecked") + Class providerClass = + (Class) maybeProvider; + Method method = providerClass.getMethod("ensureAvailability"); + method.invoke(null); + constructor = providerClass.getConstructor(); + constructor.newInstance(); + } else if (!(maybeProvider instanceof ClassNotFoundException)) { + throw (Throwable) maybeProvider; } + } catch (Throwable cause) { + LOGGER.debug( + "Unable to use MacOSDnsServerAddressStreamProvider, fallback to system defaults", cause); + constructor = null; } - return current.nameServerAddressStream(hostname); } - - private DnsServerAddressStreamProvider provider() { - // If on windows just use the DefaultDnsServerAddressStreamProvider.INSTANCE as otherwise - // we will log some error which may be confusing. - return PlatformDependent.isWindows() ? DefaultDnsServerAddressStreamProvider.INSTANCE : - UnixResolverDnsServerAddressStreamProvider.parseSilently(); - } - }; + STREAM_PROVIDER_CONSTRUCTOR = constructor; + } private DnsServerAddressStreamProviders() { } @@ -67,6 +83,53 @@ public final class DnsServerAddressStreamProviders { * configuration. */ public static DnsServerAddressStreamProvider platformDefault() { - return DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER; + if (STREAM_PROVIDER_CONSTRUCTOR != null) { + try { + return STREAM_PROVIDER_CONSTRUCTOR.newInstance(); + } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { + // ignore + } + } + return unixDefault(); + } + + public static DnsServerAddressStreamProvider unixDefault() { + return DefaultProviderHolder.DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER; + } + + // We use a Holder class to only initialize DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER if we really + // need it. + private static final class DefaultProviderHolder { + // We use 5 minutes which is the same as what OpenJDK is using in sun.net.dns.ResolverConfigurationImpl. + private static final long REFRESH_INTERVAL = TimeUnit.MINUTES.toNanos(5); + + // TODO(scott): how is this done on Windows? This may require a JNI call to GetNetworkParams + // https://msdn.microsoft.com/en-us/library/aa365968(VS.85).aspx. + static final DnsServerAddressStreamProvider DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER = + new DnsServerAddressStreamProvider() { + private volatile DnsServerAddressStreamProvider currentProvider = provider(); + private final AtomicLong lastRefresh = new AtomicLong(System.nanoTime()); + + @Override + public DnsServerAddressStream nameServerAddressStream(String hostname) { + long last = lastRefresh.get(); + DnsServerAddressStreamProvider current = currentProvider; + if (System.nanoTime() - last > REFRESH_INTERVAL) { + // This is slightly racy which means it will be possible still use the old configuration + // for a small amount of time, but that's ok. + if (lastRefresh.compareAndSet(last, System.nanoTime())) { + current = currentProvider = provider(); + } + } + return current.nameServerAddressStream(hostname); + } + + private DnsServerAddressStreamProvider provider() { + // If on windows just use the DefaultDnsServerAddressStreamProvider.INSTANCE as otherwise + // we will log some error which may be confusing. + return PlatformDependent.isWindows() ? DefaultDnsServerAddressStreamProvider.INSTANCE : + UnixResolverDnsServerAddressStreamProvider.parseSilently(); + } + }; } } diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressStreamProvidersTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressStreamProvidersTest.java new file mode 100644 index 0000000000..57be3adc68 --- /dev/null +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressStreamProvidersTest.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 The Netty Project + * + * The Netty Project licenses this file to you 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.netty.resolver.dns; + +import org.junit.Assert; +import org.junit.Test; + +public class DnsServerAddressStreamProvidersTest { + + @Test + public void testUseCorrectProvider() { + Assert.assertSame(DnsServerAddressStreamProviders.unixDefault(), + DnsServerAddressStreamProviders.platformDefault()); + } +}