Repository: netty/netty
Issue: NoClassDefFoundError on Android platform
Labels: defect
android

### Expected behavior

No exception

### Actual behavior

Exception arise during invocation DnsServerAddressStreamProviders.platformDefault method in Android application:
```java
java.lang.NoClassDefFoundError: Failed resolution of: Ljavax/naming/directory/InitialDirContext;
        at io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.<clinit>(DefaultDnsServerAddressStreamProvider.java:68)
        at io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.parseSilently(UnixResolverDnsServerAddressStreamProvider.java:76)
        at io.netty.resolver.dns.DnsServerAddressStreamProviders.<clinit>(DnsServerAddressStreamProviders.java:32)
        at io.netty.resolver.dns.DnsServerAddressStreamProviders.platformDefault(DnsServerAddressStreamProviders.java:45)
```
### Steps to reproduce

execute `DnsServerAddressStreamProviders.platformDefault` method

### Netty version

4.1.31.Final

Issue comments:
@mrniko thanks for reporting... should be fixed by https://github.com/netty/netty/pull/8656

Pull request review comments:
Optional: logging fine the the NamingExceptions was useful when developing gRPC's DNS support.

Patch that resolved the issue:
diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java
index a5ca38d368b..00be0722952 100644
--- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java
+++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java
@@ -16,23 +16,17 @@
 package io.netty.resolver.dns;
 
 import io.netty.util.NetUtil;
+import io.netty.util.internal.PlatformDependent;
 import io.netty.util.internal.SocketUtils;
 import io.netty.util.internal.UnstableApi;
 import io.netty.util.internal.logging.InternalLogger;
 import io.netty.util.internal.logging.InternalLoggerFactory;
 
-import javax.naming.Context;
-import javax.naming.NamingException;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.InitialDirContext;
 import java.lang.reflect.Method;
 import java.net.Inet6Address;
 import java.net.InetSocketAddress;
-import java.net.URI;
-import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Hashtable;
 import java.util.List;
 
 import static io.netty.resolver.dns.DnsServerAddresses.sequential;
@@ -55,41 +49,10 @@ public final class DefaultDnsServerAddressStreamProvider implements DnsServerAdd
 
     static {
         final List<InetSocketAddress> defaultNameServers = new ArrayList<InetSocketAddress>(2);
-
-        // Using jndi-dns to obtain the default name servers.
-        //
-        // See:
-        // - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html
-        // - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html
-        Hashtable<String, String> env = new Hashtable<String, String>();
-        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
-        env.put("java.naming.provider.url", "dns://");
-        try {
-            DirContext ctx = new InitialDirContext(env);
-            String dnsUrls = (String) ctx.getEnvironment().get("java.naming.provider.url");
-            // Only try if not empty as otherwise we will produce an exception
-            if (dnsUrls != null && !dnsUrls.isEmpty()) {
-                String[] servers = dnsUrls.split(" ");
-                for (String server : servers) {
-                    try {
-                        URI uri = new URI(server);
-                        String host = new URI(server).getHost();
-
-                        if (host == null || host.isEmpty()) {
-                            logger.debug(
-                                    "Skipping a nameserver URI as host portion could not be extracted: {}", server);
-                            // If the host portion can not be parsed we should just skip this entry.
-                            continue;
-                        }
-                        int port  = uri.getPort();
-                        defaultNameServers.add(SocketUtils.socketAddress(uri.getHost(), port == -1 ? DNS_PORT : port));
-                    } catch (URISyntaxException e) {
-                        logger.debug("Skipping a malformed nameserver URI: {}", server, e);
-                    }
-                }
-            }
-        } catch (NamingException ignore) {
-            // Will try reflection if this fails.
+        if (!PlatformDependent.isAndroid()) {
+            // Only try to use when not on Android as the classes not exists there:
+            // See https://github.com/netty/netty/issues/8654
+            DirContextUtils.addNameServers(defaultNameServers, DNS_PORT);
         }
 
         if (defaultNameServers.isEmpty()) {
diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DirContextUtils.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DirContextUtils.java
new file mode 100644
index 00000000000..45c1ac433f2
--- /dev/null
+++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DirContextUtils.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 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 io.netty.util.internal.SocketUtils;
+import io.netty.util.internal.logging.InternalLogger;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Hashtable;
+import java.util.List;
+
+final class DirContextUtils {
+    private static final InternalLogger logger =
+            InternalLoggerFactory.getInstance(DirContextUtils.class);
+
+    private DirContextUtils() { }
+
+    static void addNameServers(List<InetSocketAddress> defaultNameServers, int defaultPort) {
+        // Using jndi-dns to obtain the default name servers.
+        //
+        // See:
+        // - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html
+        // - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
+        env.put("java.naming.provider.url", "dns://");
+
+        try {
+            DirContext ctx = new InitialDirContext(env);
+            String dnsUrls = (String) ctx.getEnvironment().get("java.naming.provider.url");
+            // Only try if not empty as otherwise we will produce an exception
+            if (dnsUrls != null && !dnsUrls.isEmpty()) {
+                String[] servers = dnsUrls.split(" ");
+                for (String server : servers) {
+                    try {
+                        URI uri = new URI(server);
+                        String host = new URI(server).getHost();
+
+                        if (host == null || host.isEmpty()) {
+                            logger.debug(
+                                    "Skipping a nameserver URI as host portion could not be extracted: {}", server);
+                            // If the host portion can not be parsed we should just skip this entry.
+                            continue;
+                        }
+                        int port  = uri.getPort();
+                        defaultNameServers.add(SocketUtils.socketAddress(uri.getHost(), port == -1 ?
+                                defaultPort : port));
+                    } catch (URISyntaxException e) {
+                        logger.debug("Skipping a malformed nameserver URI: {}", server, e);
+                    }
+                }
+            }
+        } catch (NamingException ignore) {
+            // Will try reflection if this fails.
+        }
+    }
+}


Test patch: